Merge "Customization of LTE Signal Bars"
diff --git a/Android.mk b/Android.mk
index 6c9b190..db5dd01 100644
--- a/Android.mk
+++ b/Android.mk
@@ -116,9 +116,10 @@
 	core/java/android/bluetooth/IBluetoothPan.aidl \
 	core/java/android/bluetooth/IBluetoothManager.aidl \
 	core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
+	core/java/android/bluetooth/IBluetoothMap.aidl \
+	core/java/android/bluetooth/IBluetoothMapClient.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothPbapClient.aidl \
-	core/java/android/bluetooth/IBluetoothMap.aidl \
 	core/java/android/bluetooth/IBluetoothSap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
@@ -126,6 +127,7 @@
 	core/java/android/bluetooth/IBluetoothGattCallback.aidl \
 	core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
 	core/java/android/bluetooth/le/IAdvertiserCallback.aidl \
+	core/java/android/bluetooth/le/IScannerCallback.aidl \
 	core/java/android/content/IClipboard.aidl \
 	core/java/android/content/IContentService.aidl \
 	core/java/android/content/IIntentReceiver.aidl \
@@ -450,9 +452,9 @@
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
-	wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl \
-	wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
-	wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
 	wifi/java/android/net/wifi/IWifiScanner.aidl \
 	wifi/java/android/net/wifi/IRttManager.aidl \
@@ -535,9 +537,9 @@
 	frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
 	frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
 	frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/PublishConfig.aidl \
-	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl \
+	frameworks/base/wifi/java/android/net/wifi/aware/ConfigRequest.aidl \
+	frameworks/base/wifi/java/android/net/wifi/aware/PublishConfig.aidl \
+	frameworks/base/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
diff --git a/api/current.txt b/api/current.txt
index b391b00..21ff787 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36363,6 +36363,7 @@
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -36864,8 +36865,12 @@
     method public int describeContents();
     method public boolean equals(java.lang.Object);
     method public int getAsuLevel();
+    method public int getCqi();
     method public int getDbm();
     method public int getLevel();
+    method public int getRsrp();
+    method public int getRsrq();
+    method public int getRssnr();
     method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
@@ -37211,6 +37216,7 @@
 
   public class TelephonyManager {
     method public boolean canChangeDtmfToneLength();
+    method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
@@ -37233,6 +37239,7 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
+    method public android.telephony.ServiceState getServiceState();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
     method public java.lang.String getSimOperatorName();
@@ -62686,6 +62693,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CRC32 implements java.util.zip.Checksum {
@@ -62695,6 +62703,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CheckedInputStream extends java.io.FilterInputStream {
@@ -62841,7 +62850,10 @@
     method public java.lang.String getComment();
     method public long getCompressedSize();
     method public long getCrc();
+    method public java.nio.file.attribute.FileTime getCreationTime();
     method public byte[] getExtra();
+    method public java.nio.file.attribute.FileTime getLastAccessTime();
+    method public java.nio.file.attribute.FileTime getLastModifiedTime();
     method public int getMethod();
     method public java.lang.String getName();
     method public long getSize();
@@ -62850,7 +62862,10 @@
     method public void setComment(java.lang.String);
     method public void setCompressedSize(long);
     method public void setCrc(long);
+    method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
     method public void setExtra(byte[]);
+    method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+    method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
     method public void setMethod(int);
     method public void setSize(long);
     method public void setTime(long);
@@ -62921,6 +62936,7 @@
     method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
     method public java.lang.String getName();
     method public int size();
+    method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
     field public static final int CENATT = 36; // 0x24
     field public static final int CENATX = 38; // 0x26
     field public static final int CENCOM = 32; // 0x20
diff --git a/api/system-current.txt b/api/system-current.txt
index 68c7b48..6645d53 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39267,6 +39267,7 @@
     field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -39799,8 +39800,12 @@
     method public int describeContents();
     method public boolean equals(java.lang.Object);
     method public int getAsuLevel();
+    method public int getCqi();
     method public int getDbm();
     method public int getLevel();
+    method public int getRsrp();
+    method public int getRsrq();
+    method public int getRssnr();
     method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
@@ -40150,6 +40155,7 @@
     method public boolean canChangeDtmfToneLength();
     method public int checkCarrierPrivilegesForPackage(java.lang.String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
+    method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public void dial(java.lang.String);
     method public boolean disableDataConnectivity();
@@ -40187,6 +40193,7 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
+    method public android.telephony.ServiceState getServiceState();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
     method public java.lang.String getSimOperatorName();
@@ -66043,6 +66050,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CRC32 implements java.util.zip.Checksum {
@@ -66052,6 +66060,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CheckedInputStream extends java.io.FilterInputStream {
@@ -66198,7 +66207,10 @@
     method public java.lang.String getComment();
     method public long getCompressedSize();
     method public long getCrc();
+    method public java.nio.file.attribute.FileTime getCreationTime();
     method public byte[] getExtra();
+    method public java.nio.file.attribute.FileTime getLastAccessTime();
+    method public java.nio.file.attribute.FileTime getLastModifiedTime();
     method public int getMethod();
     method public java.lang.String getName();
     method public long getSize();
@@ -66207,7 +66219,10 @@
     method public void setComment(java.lang.String);
     method public void setCompressedSize(long);
     method public void setCrc(long);
+    method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
     method public void setExtra(byte[]);
+    method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+    method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
     method public void setMethod(int);
     method public void setSize(long);
     method public void setTime(long);
@@ -66278,6 +66293,7 @@
     method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
     method public java.lang.String getName();
     method public int size();
+    method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
     field public static final int CENATT = 36; // 0x24
     field public static final int CENATX = 38; // 0x26
     field public static final int CENCOM = 32; // 0x20
diff --git a/api/test-current.txt b/api/test-current.txt
index 2aa6cfe..5301443 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36440,6 +36440,7 @@
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -36941,8 +36942,12 @@
     method public int describeContents();
     method public boolean equals(java.lang.Object);
     method public int getAsuLevel();
+    method public int getCqi();
     method public int getDbm();
     method public int getLevel();
+    method public int getRsrp();
+    method public int getRsrq();
+    method public int getRssnr();
     method public int getTimingAdvance();
     method public int hashCode();
     method public void writeToParcel(android.os.Parcel, int);
@@ -37288,6 +37293,7 @@
 
   public class TelephonyManager {
     method public boolean canChangeDtmfToneLength();
+    method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
@@ -37310,6 +37316,7 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
+    method public android.telephony.ServiceState getServiceState();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
     method public java.lang.String getSimOperatorName();
@@ -62766,6 +62773,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CRC32 implements java.util.zip.Checksum {
@@ -62775,6 +62783,7 @@
     method public void update(int);
     method public void update(byte[], int, int);
     method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
   }
 
   public class CheckedInputStream extends java.io.FilterInputStream {
@@ -62921,7 +62930,10 @@
     method public java.lang.String getComment();
     method public long getCompressedSize();
     method public long getCrc();
+    method public java.nio.file.attribute.FileTime getCreationTime();
     method public byte[] getExtra();
+    method public java.nio.file.attribute.FileTime getLastAccessTime();
+    method public java.nio.file.attribute.FileTime getLastModifiedTime();
     method public int getMethod();
     method public java.lang.String getName();
     method public long getSize();
@@ -62930,7 +62942,10 @@
     method public void setComment(java.lang.String);
     method public void setCompressedSize(long);
     method public void setCrc(long);
+    method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
     method public void setExtra(byte[]);
+    method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+    method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
     method public void setMethod(int);
     method public void setSize(long);
     method public void setTime(long);
@@ -63001,6 +63016,7 @@
     method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
     method public java.lang.String getName();
     method public int size();
+    method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
     field public static final int CENATT = 36; // 0x24
     field public static final int CENATX = 38; // 0x26
     field public static final int CENCOM = 32; // 0x20
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 18ad43e..d5580ac 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -188,6 +188,16 @@
         LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
     }
 
+    if (!LOG_NDEBUG) {
+      String8 argv_String;
+      for (int i = 0; i < argc; ++i) {
+        argv_String.append("\"");
+        argv_String.append(argv[i]);
+        argv_String.append("\" ");
+      }
+      ALOGV("app_process main with argv: %s", argv_String.string());
+    }
+
     AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
     // Process command line arguments
     // ignore argv[0]
@@ -216,9 +226,31 @@
     //
     // Note that we must copy argument string values since we will rewrite the
     // entire argument block when we apply the nice name to argv0.
+    //
+    // As an exception to the above rule, anything in "spaced commands"
+    // goes to the vm even though it has a space in it.
+    const char* spaced_commands[] = { "-cp", "-classpath" };
+    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
+    bool known_command = false;
 
     int i;
     for (i = 0; i < argc; i++) {
+        if (known_command == true) {
+          runtime.addOption(strdup(argv[i]));
+          ALOGV("app_process main add known option '%s'", argv[i]);
+          known_command = false;
+          continue;
+        }
+
+        for (int j = 0;
+             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
+             ++j) {
+          if (strcmp(argv[i], spaced_commands[j]) == 0) {
+            known_command = true;
+            ALOGV("app_process main found known command '%s'", argv[i]);
+          }
+        }
+
         if (argv[i][0] != '-') {
             break;
         }
@@ -226,7 +258,9 @@
             ++i; // Skip --.
             break;
         }
+
         runtime.addOption(strdup(argv[i]));
+        ALOGV("app_process main add option '%s'", argv[i]);
     }
 
     // Parse runtime arguments.  Stop at first unrecognized option.
@@ -266,6 +300,18 @@
         // copies of them before we overwrite them with the process name.
         args.add(application ? String8("application") : String8("tool"));
         runtime.setClassNameAndArgs(className, argc - i, argv + i);
+
+        if (!LOG_NDEBUG) {
+          String8 restOfArgs;
+          char* const* argv_new = argv + i;
+          int argc_new = argc - i;
+          for (int k = 0; k < argc_new; ++k) {
+            restOfArgs.append("\"");
+            restOfArgs.append(argv_new[k]);
+            restOfArgs.append("\" ");
+          }
+          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
+        }
     } else {
         // We're in zygote mode.
         maybeCreateDalvikCache();
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index 6d30f0d..ab6adfb 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -1,5 +1,6 @@
 #include <dirent.h>
 #include <inttypes.h>
+#include <sys/file.h>
 #include <sys/stat.h>
 
 #include "idmap.h"
@@ -35,16 +36,31 @@
 
     bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
     {
-        FILE* fout = fopen(filename, "w");
+        // the file is opened for appending so that it doesn't get truncated
+        // before we can guarantee mutual exclusion via the flock
+        FILE* fout = fopen(filename, "a");
         if (fout == NULL) {
             return false;
         }
 
+        if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
+            fclose(fout);
+            return false;
+        }
+
+        if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
+            TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
+            fclose(fout);
+            return false;
+        }
+
         for (size_t i = 0; i < overlayVector.size(); ++i) {
             const Overlay& overlay = overlayVector[i];
             fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
         }
 
+        TEMP_FAILURE_RETRY(fflush(fout));
+        TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
         fclose(fout);
 
         // Make file world readable since Zygote (running as root) will read
@@ -171,9 +187,6 @@
 {
     String8 filename = String8(idmap_dir);
     filename.appendPath("overlays.list");
-    if (unlink(filename.string()) != 0 && errno != ENOENT) {
-        return EXIT_FAILURE;
-    }
 
     SortedVector<Overlay> overlayVector;
     const size_t N = overlay_dirs->size();
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index a6ef25f..4dcb05e 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -50,7 +50,7 @@
                 IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
                         Context.USB_SERVICE));
                 try {
-                    usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null));
+                    usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), false);
                 } catch (RemoteException e) {
                     System.err.println("Error communicating with UsbManager: " + e);
                 }
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
index 94214ff..633dd97 100644
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
@@ -52,7 +52,7 @@
                 IWifiManager wifiMgr
                         = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
                 try {
-                    wifiMgr.setWifiEnabled(flag);
+                    wifiMgr.setWifiEnabled("com.android.shell", flag);
                 }
                 catch (RemoteException e) {
                     System.err.println("Wi-Fi operation failed: " + e);
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
new file mode 100644
index 0000000..66e762c
--- /dev/null
+++ b/cmds/webview_zygote/Android.mk
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := webview_zygote
+
+LOCAL_SRC_FILES := webview_zygote.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libandroid_runtime \
+	libbinder \
+	liblog \
+	libcutils \
+	libutils
+
+LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+
+LOCAL_INIT_RC := webview_zygote32.rc
+
+# Always include the 32-bit version of webview_zygote. If the target is 64-bit,
+# also include the 64-bit webview_zygote.
+ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
+	LOCAL_INIT_RC += webview_zygote64.rc
+endif
+
+LOCAL_MULTILIB := both
+
+LOCAL_MODULE_STEM_32 := webview_zygote32
+LOCAL_MODULE_STEM_64 := webview_zygote64
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/webview_zygote/webview_zygote.cpp b/cmds/webview_zygote/webview_zygote.cpp
new file mode 100644
index 0000000..88fee64
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+
+#define LOG_TAG "WebViewZygote"
+
+#include <sys/prctl.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class WebViewRuntime : public AndroidRuntime {
+public:
+    WebViewRuntime(char* argBlockStart, size_t argBlockSize)
+        : AndroidRuntime(argBlockStart, argBlockSize) {}
+
+    ~WebViewRuntime() override {}
+
+    void onStarted() override {
+        // Nothing to do since this is a zygote server.
+    }
+
+    void onVmCreated(JNIEnv*) override {
+        // Nothing to do when the VM is created in the zygote.
+    }
+
+    void onZygoteInit() override {
+        // Called after a new process is forked.
+        sp<ProcessState> proc = ProcessState::self();
+        proc->startThreadPool();
+    }
+
+    void onExit(int code) override {
+        IPCThreadState::self()->stopProcess();
+        AndroidRuntime::onExit(code);
+    }
+};
+
+}  // namespace android
+
+int main(int argc, char* const argv[]) {
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+        return 12;
+    }
+
+    size_t argBlockSize = 0;
+    for (int i = 0; i < argc; ++i) {
+        argBlockSize += strlen(argv[i]) + 1;
+    }
+
+    android::WebViewRuntime runtime(argv[0], argBlockSize);
+    runtime.addOption("-Xzygote");
+
+    android::Vector<android::String8> args;
+    runtime.start("com.android.internal.os.WebViewZygoteInit", args, /*zygote=*/ true);
+}
diff --git a/cmds/webview_zygote/webview_zygote32.rc b/cmds/webview_zygote/webview_zygote32.rc
new file mode 100644
index 0000000..b7decc8
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote32.rc
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2016 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.
+#
+
+service webview_zygote32 /system/bin/webview_zygote32
+    user webview_zygote
+    socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+    stop webview_zygote32
diff --git a/cmds/webview_zygote/webview_zygote64.rc b/cmds/webview_zygote/webview_zygote64.rc
new file mode 100644
index 0000000..2935b28
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote64.rc
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2016 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.
+#
+
+service webview_zygote64 /system/bin/webview_zygote64
+    user webview_zygote
+    socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+    stop webview_zygote64
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 83dab16..d242ba1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -965,6 +965,10 @@
             sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
         }
 
+        public void attachAgent(String agent) {
+            sendMessage(H.ATTACH_AGENT, agent);
+        }
+
         public void setSchedulingGroup(int group) {
             // Note: do this immediately, since going into the foreground
             // should happen regardless of what pending work we have to do
@@ -1388,6 +1392,7 @@
         public static final int MULTI_WINDOW_MODE_CHANGED = 152;
         public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
         public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
+        public static final int ATTACH_AGENT = 155;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1444,6 +1449,7 @@
                     case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
                     case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
                     case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
+                    case ATTACH_AGENT: return "ATTACH_AGENT";
                 }
             }
             return Integer.toString(code);
@@ -1696,6 +1702,8 @@
                 case LOCAL_VOICE_INTERACTION_STARTED:
                     handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
                             (IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
+                case ATTACH_AGENT:
+                    handleAttachAgent((String) msg.obj);
                     break;
             }
             Object obj = msg.obj;
@@ -2955,6 +2963,14 @@
         }
     }
 
+    static final void handleAttachAgent(String agent) {
+        try {
+            VMDebug.attachAgent(agent);
+        } catch (IOException e) {
+            Slog.e(TAG, "Attaching agent failed: " + agent);
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
@@ -5133,7 +5149,14 @@
         }
         updateDefaultDensity();
 
-        final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
+        final String use24HourSetting = mCoreSettings.getString(Settings.System.TIME_12_24);
+        Boolean is24Hr = null;
+        if (use24HourSetting != null) {
+            is24Hr = "24".equals(use24HourSetting) ? Boolean.TRUE : Boolean.FALSE;
+        }
+        // null : use locale default for 12/24 hour formatting,
+        // false : use 12 hour format,
+        // true : use 24 hour format.
         DateFormat.set24HourTimePref(is24Hr);
 
         View.mDebugViewAttributes =
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index d6da3f4..a8313d1 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -501,6 +501,14 @@
             return true;
         }
 
+        case ATTACH_AGENT_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            String agent = data.readString();
+            attachAgent(agent);
+            return true;
+        }
+
         case DUMP_ACTIVITY_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -1303,6 +1311,14 @@
         data.recycle();
     }
 
+    public void attachAgent(String agent) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeString(agent);
+        mRemote.transact(ATTACH_AGENT_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
     public void setCoreSettings(Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 559f69f..5b13164 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -122,6 +122,7 @@
             throws RemoteException;
     void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
             throws RemoteException;
+    void attachAgent(String path) throws RemoteException;
     void setSchedulingGroup(int group) throws RemoteException;
     // the package has been removed, clean up internal references
     static final int PACKAGE_REMOVED = 0;
@@ -224,4 +225,5 @@
     int SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
     int SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
     int SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60;
+    int ATTACH_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61;
 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index c4673a3..744bd30 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -793,7 +793,8 @@
 
             for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
                 ResourcesKey key = mResourceImpls.keyAt(i);
-                ResourcesImpl r = mResourceImpls.valueAt(i).get();
+                WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+                ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
                 if (r != null) {
                     if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                             + r + " config to: " + config);
@@ -854,8 +855,9 @@
 
             final int implCount = mResourceImpls.size();
             for (int i = 0; i < implCount; i++) {
-                final ResourcesImpl impl = mResourceImpls.valueAt(i).get();
                 final ResourcesKey key = mResourceImpls.keyAt(i);
+                final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+                final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
                 if (impl != null && key.mResDir.equals(assetPath)) {
                     if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
                         final int newLibAssetCount = 1 +
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 42ddf10..4c5fb13 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -84,8 +84,8 @@
 import android.net.wifi.RttManager;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
-import android.net.wifi.nan.IWifiNanManager;
-import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.aware.IWifiAwareManager;
+import android.net.wifi.aware.WifiAwareManager;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.nfc.NfcManager;
@@ -514,16 +514,16 @@
                 return new WifiP2pManager(service);
             }});
 
-        registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class,
-                new CachedServiceFetcher<WifiNanManager>() {
+        registerService(Context.WIFI_AWARE_SERVICE, WifiAwareManager.class,
+                new CachedServiceFetcher<WifiAwareManager>() {
             @Override
-            public WifiNanManager createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE);
-                IWifiNanManager service = IWifiNanManager.Stub.asInterface(b);
+            public WifiAwareManager createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(Context.WIFI_AWARE_SERVICE);
+                IWifiAwareManager service = IWifiAwareManager.Stub.asInterface(b);
                 if (service == null) {
                     return null;
                 }
-                return new WifiNanManager(ctx.getOuterContext(), service);
+                return new WifiAwareManager(ctx.getOuterContext(), service);
             }});
 
         registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cd1a3dd..4271e3f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -23,6 +23,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.app.ActivityThread;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
@@ -252,6 +253,29 @@
             "android.bluetooth.adapter.action.REQUEST_ENABLE";
 
     /**
+     * Activity Action: Show a system activity that allows the user to turn off
+     * Bluetooth. This is used only if permission review is enabled which is for
+     * apps targeting API less than 23 require a permission review before any of
+     * the app's components can run.
+     * <p>This system activity will return once Bluetooth has completed turning
+     * off, or the user has decided not to turn Bluetooth off.
+     * <p>Notification of the result of this activity is posted using the
+     * {@link android.app.Activity#onActivityResult} callback. The
+     * <code>resultCode</code>
+     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
+     * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user
+     * has rejected the request or an error has occurred.
+     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
+     * for global notification whenever Bluetooth is turned on or off.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_DISABLE =
+            "android.bluetooth.adapter.action.REQUEST_DISABLE";
+
+    /**
      * Activity Action: Show a system activity that allows user to enable BLE scans even when
      * Bluetooth is turned off.<p>
      *
@@ -577,10 +601,6 @@
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
         if (!getLeAccess()) return null;
-        if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
-            Log.e(TAG, "Bluetooth LE advertising not supported");
-            return null;
-        }
         synchronized(mLock) {
             if (sBluetoothLeAdvertiser == null) {
                 sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
@@ -762,20 +782,14 @@
     public boolean enableBLE() {
         if (!isBleScanAlwaysAvailable()) return false;
 
-        if (isLeEnabled() == true) {
-            if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
-            try {
-                mManagerService.updateBleAppCount(mToken, true);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-            return true;
-        }
-
         try {
-            if (DBG) Log.d(TAG, "Calling enableBLE");
             mManagerService.updateBleAppCount(mToken, true);
-            return mManagerService.enable();
+            if (isLeEnabled()) {
+                if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                return true;
+            }
+            if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
+            return mManagerService.enable(ActivityThread.currentPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
@@ -902,7 +916,7 @@
             return true;
         }
         try {
-            return mManagerService.enable();
+            return mManagerService.enable(ActivityThread.currentPackageName());
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
@@ -934,7 +948,7 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean disable() {
         try {
-            return mManagerService.disable(true);
+            return mManagerService.disable(ActivityThread.currentPackageName(), true);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
@@ -952,7 +966,7 @@
     public boolean disable(boolean persist) {
 
         try {
-            return mManagerService.disable(persist);
+            return mManagerService.disable(ActivityThread.currentPackageName(), persist);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
@@ -1336,24 +1350,6 @@
     }
 
     /**
-     * Returns whether peripheral mode is supported.
-     *
-     * @hide
-     */
-    public boolean isPeripheralModeSupported() {
-        if (getState() != STATE_ON) return false;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.isPeripheralModeSupported();
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get peripheral mode capability: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
      * Return true if offloaded filters are supported
      *
      * @return true if chipset supports on-chip filtering
@@ -1495,6 +1491,36 @@
     }
 
     /**
+     * Gets the currently supported profiles by the adapter.
+     *
+     *<p> This can be used to check whether a profile is supported before attempting
+     * to connect to its respective proxy.
+     *
+     * @return a list of integers indicating the ids of supported profiles as defined in
+     * {@link BluetoothProfile}.
+     * @hide
+     */
+    public List<Integer> getSupportedProfiles() {
+        final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
+
+        try {
+            synchronized (mManagerCallback) {
+                if (mService != null) {
+                    final long supportedProfilesBitMask = mService.getSupportedProfiles();
+
+                    for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
+                        if ((supportedProfilesBitMask & (1 << i)) != 0) {
+                            supportedProfiles.add(i);
+                        }
+                    }
+                }
+            }
+        } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);}
+
+        return supportedProfiles;
+    }
+
+    /**
      * Get the current connection state of the local Bluetooth adapter.
      * This can be used to check whether the local Bluetooth adapter is connected
      * to any profile of any other remote Bluetooth Device.
@@ -1925,6 +1951,9 @@
         } else if (profile == BluetoothProfile.PBAP_CLIENT) {
             BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.MAP_CLIENT) {
+            BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -1997,6 +2026,10 @@
                 BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy;
                 pbapClient.close();
                 break;
+            case BluetoothProfile.MAP_CLIENT:
+                BluetoothMapClient mapClient = (BluetoothMapClient)proxy;
+                mapClient.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 444e429..a395aa4 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -66,21 +66,6 @@
         "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
 
     /**
-     * Intent used to broadcast the change in metadata state of playing track on the AVRCP
-     * AG.
-     *
-     * <p>This intent will have the two extras:
-     * <ul>
-     *    <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li>
-     *    <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback
-     *    state. </li>
-     * </ul>
-     */
-    public static final String ACTION_TRACK_EVENT =
-        "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";
-
-
-    /**
      * Intent used to broadcast the change in player application setting state on AVRCP AG.
      *
      * <p>This intent will have the following extras:
@@ -92,35 +77,9 @@
     public static final String ACTION_PLAYER_SETTING =
         "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
 
-    public static final String EXTRA_METADATA =
-            "android.bluetooth.avrcp-controller.profile.extra.METADATA";
-
-    public static final String EXTRA_PLAYBACK =
-            "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
-
     public static final String EXTRA_PLAYER_SETTING =
             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
 
-    /*
-     * KeyCoded for Pass Through Commands
-     */
-    public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
-    public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
-    public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
-    public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
-    public static final int PASS_THRU_CMD_ID_STOP = 0x45;
-    public static final int PASS_THRU_CMD_ID_FF = 0x49;
-    public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
-    public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
-    public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
-    /* Key State Variables */
-    public static final int KEY_STATE_PRESSED = 0;
-    public static final int KEY_STATE_RELEASED = 1;
-    /* Group Navigation Key Codes */
-    public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
-    public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;
-
-
     private Context mContext;
     private ServiceListener mServiceListener;
     private IBluetoothAvrcpController mService;
@@ -267,20 +226,6 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
-    public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
-        if (DBG) Log.d(TAG, "sendPassThroughCmd");
-        if (mService != null && isEnabled()) {
-            try {
-                mService.sendPassThroughCmd(device, keyCode, keyState);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e);
-                return;
-            }
-        }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-    }
-
     /**
      * Gets the player application settings.
      *
@@ -301,49 +246,6 @@
     }
 
     /**
-     * Gets the metadata for the current track.
-     *
-     * This should be usually called when application UI needs to be updated, eg. when the track
-     * changes or immediately after connecting and getting the current state.
-     * @return the {@link MediaMetadata} or {@link null} if there is an error.
-     */
-    public MediaMetadata getMetadata(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getMetadata");
-        MediaMetadata metadata = null;
-        if (mService != null && isEnabled()) {
-            try {
-                metadata = mService.getMetadata(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
-                return null;
-            }
-        }
-        return metadata;
-    }
-
-    /**
-     * Gets the playback state for current track.
-     *
-     * When the application is first connecting it can use current track state to get playback info.
-     * For all further updates it should listen to notifications.
-     * @return the {@link PlaybackState} or {@link null} if there is an error.
-     */
-    public PlaybackState getPlaybackState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getPlaybackState");
-        PlaybackState playbackState = null;
-        if (mService != null && isEnabled()) {
-            try {
-                playbackState = mService.getPlaybackState(device);
-            } catch (RemoteException e) {
-                Log.e(TAG,
-                    "Error talking to BT service in getPlaybackState() " + e);
-                return null;
-            }
-        }
-        return playbackState;
-    }
-
-    /**
      * Sets the player app setting for current player.
      * returns true in case setting is supported by remote, false otherwise
      */
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 9cfe417..f76d62d 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -45,14 +45,18 @@
     private IBluetoothGatt mService;
     private BluetoothGattCallback mCallback;
     private int mClientIf;
-    private boolean mAuthRetry = false;
     private BluetoothDevice mDevice;
     private boolean mAutoConnect;
+    private int mAuthRetryState;
     private int mConnState;
     private final Object mStateLock = new Object();
     private Boolean mDeviceBusy = false;
     private int mTransport;
 
+    private static final int AUTH_RETRY_STATE_IDLE = 0;
+    private static final int AUTH_RETRY_STATE_NO_MITM = 1;
+    private static final int AUTH_RETRY_STATE_MITM = 2;
+
     private static final int CONN_STATE_IDLE = 0;
     private static final int CONN_STATE_CONNECTING = 1;
     private static final int CONN_STATE_CONNECTED = 2;
@@ -132,7 +136,7 @@
      * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
      */
     private final IBluetoothGattCallback mBluetoothGattCallback =
-        new BluetoothGattCallbackWrapper() {
+        new IBluetoothGattCallback.Stub() {
             /**
              * Application interface registered - app is ready to go
              * @hide
@@ -260,17 +264,19 @@
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                   || status == GATT_INSUFFICIENT_ENCRYPTION)
-                  && mAuthRetry == false) {
+                  && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
                     try {
-                        mAuthRetry = true;
-                        mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
+                        final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+                                AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
+                        mService.readCharacteristic(mClientIf, address, handle, authReq);
+                        mAuthRetryState++;
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
                     }
                 }
 
-                mAuthRetry = false;
+                mAuthRetryState = AUTH_RETRY_STATE_IDLE;
 
                 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
                 if (characteristic == null) {
@@ -309,19 +315,20 @@
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                   || status == GATT_INSUFFICIENT_ENCRYPTION)
-                  && mAuthRetry == false) {
+                  && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
                     try {
-                        mAuthRetry = true;
+                        final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+                                AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
                         mService.writeCharacteristic(mClientIf, address, handle,
-                            characteristic.getWriteType(), AUTHENTICATION_MITM,
-                            characteristic.getValue());
+                            characteristic.getWriteType(), authReq, characteristic.getValue());
+                        mAuthRetryState++;
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
                     }
                 }
 
-                mAuthRetry = false;
+                mAuthRetryState = AUTH_RETRY_STATE_IDLE;
 
                 try {
                     mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
@@ -376,17 +383,19 @@
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                   || status == GATT_INSUFFICIENT_ENCRYPTION)
-                  && mAuthRetry == false) {
+                  && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
                     try {
-                        mAuthRetry = true;
-                        mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
+                        final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+                                AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
+                        mService.readDescriptor(mClientIf, address, handle, authReq);
+                        mAuthRetryState++;
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
                     }
                 }
 
-                mAuthRetry = true;
+                mAuthRetryState = AUTH_RETRY_STATE_IDLE;
 
                 try {
                     mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
@@ -415,18 +424,20 @@
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                   || status == GATT_INSUFFICIENT_ENCRYPTION)
-                  && mAuthRetry == false) {
+                  && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
                     try {
-                        mAuthRetry = true;
+                        final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+                                AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
                         mService.writeDescriptor(mClientIf, address, handle,
-                            AUTHENTICATION_MITM, descriptor.getValue());
+                            authReq, descriptor.getValue());
+                        mAuthRetryState++;
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
                     }
                 }
 
-                mAuthRetry = false;
+                mAuthRetryState = AUTH_RETRY_STATE_IDLE;
 
                 try {
                     mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
@@ -501,6 +512,7 @@
         mServices = new ArrayList<BluetoothGattService>();
 
         mConnState = CONN_STATE_IDLE;
+        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
     }
 
     /**
@@ -514,6 +526,7 @@
 
         unregisterApp();
         mConnState = CONN_STATE_CLOSED;
+        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
deleted file mode 100644
index da81569..0000000
--- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.bluetooth;
-
-import android.bluetooth.le.AdvertiseSettings;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.BluetoothGattService;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * Wrapper class for default implementation of IBluetoothGattCallback.
- *
- * @hide
- */
-public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub {
-
-    @Override
-    public void onClientRegistered(int status, int clientIf) throws RemoteException {
-    }
-
-    @Override
-    public void onClientConnectionState(int status, int clientIf, boolean connected, String address)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onScanResult(ScanResult scanResult) throws RemoteException {
-    }
-
-    @Override
-    public void onBatchScanResults(List<ScanResult> batchResults) throws RemoteException {
-    }
-
-    @Override
-    public void onSearchComplete(String address, List<BluetoothGattService> services,
-            int status) throws RemoteException {
-    }
-
-    @Override
-    public void onCharacteristicRead(String address, int status, int handle, byte[] value)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onCharacteristicWrite(String address, int status, int handle) throws RemoteException {
-    }
-
-    @Override
-    public void onExecuteWrite(String address, int status) throws RemoteException {
-    }
-
-    @Override
-    public void onDescriptorRead(String address, int status, int handle, byte[] value) throws RemoteException {
-    }
-
-    @Override
-    public void onDescriptorWrite(String address, int status, int handle) throws RemoteException {
-    }
-
-    @Override
-    public void onNotify(String address, int handle, byte[] value) throws RemoteException {
-    }
-
-    @Override
-    public void onReadRemoteRssi(String address, int rssi, int status) throws RemoteException {
-    }
-
-    @Override
-    public void onConfigureMTU(String address, int mtu, int status) throws RemoteException {
-    }
-
-    @Override
-    public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException {
-    }
-
-    @Override
-    public void onScanManagerErrorCallback(int errorCode) throws RemoteException {
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 874026f..93790fe 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -27,6 +27,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 /**
  * Public API to control Hands Free Profile (HFP role only).
@@ -799,7 +800,9 @@
      * Works only when Extended Call Control is supported by Audio Gateway.
      *
      * @param device    remote device
-     * @param index     index of the call to be terminated
+     * @param call      Handle of call obtained in {@link dial()} or obtained via
+     *                  {@link ACTION_CALL_CHANGED}. {@code call} may be null in which
+     *                  case we will hangup all active calls.
      * @return          <code>true</code> if command has been issued successfully;
      *                   <code>false</code> otherwise;
      *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
@@ -809,12 +812,12 @@
      * {@link #EXTRA_AG_FEATURE_ECC}.
      * This method invocation will fail silently when feature is not supported.</p>
      */
-    public boolean terminateCall(BluetoothDevice device, int index) {
+    public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
         if (DBG) log("terminateCall()");
         if (mService != null && isEnabled() &&
                 isValidDevice(device)) {
             try {
-                return mService.terminateCall(device, index);
+                return mService.terminateCall(device, call);
             } catch (RemoteException e) {
                 Log.e(TAG,  Log.getStackTraceString(new Throwable()));
             }
@@ -883,41 +886,18 @@
     }
 
     /**
-     * Redials last number from Audio Gateway.
-     *
-     * @param device    remote device
-     * @return          <code>true</code> if command has been issued successfully;
-     *                   <code>false</code> otherwise;
-     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
-     *                   intent in case of success; {@link #ACTION_RESULT} is sent
-     *                   otherwise;
-     */
-    public boolean redial(BluetoothDevice device) {
-        if (DBG) log("redial()");
-        if (mService != null && isEnabled() &&
-                isValidDevice(device)) {
-            try {
-                return mService.redial(device);
-            } catch (RemoteException e) {
-                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
-            }
-        }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
-    }
-
-    /**
      * Places a call with specified number.
      *
      * @param device    remote device
      * @param number    valid phone number
-     * @return          <code>true</code> if command has been issued successfully;
-     *                   <code>false</code> otherwise;
-     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
-     *                   intent in case of success; {@link #ACTION_RESULT} is sent
-     *                   otherwise;
+     * @return          <code>{@link BluetoothHeadsetClientCall} call</code> if command has been
+     *                  issued successfully;
+     *                  <code>{@link null}</code> otherwise;
+     *                  upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                  intent in case of success; {@link #ACTION_RESULT} is sent
+     *                  otherwise;
      */
-    public boolean dial(BluetoothDevice device, String number) {
+    public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
         if (DBG) log("dial()");
         if (mService != null && isEnabled() &&
                 isValidDevice(device)) {
@@ -928,32 +908,7 @@
             }
         }
         if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
-    }
-
-    /**
-     * Places a call to the number under specified memory location.
-     *
-     * @param device    remote device
-     * @param location  valid memory location
-     * @return          <code>true</code> if command has been issued successfully;
-     *                   <code>false</code> otherwise;
-     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
-     *                   intent in case of success; {@link #ACTION_RESULT} is sent
-     *                   otherwise;
-     */
-    public boolean dialMemory(BluetoothDevice device, int location) {
-        if (DBG) log("dialMemory()");
-        if (mService != null && isEnabled() &&
-                isValidDevice(device)) {
-            try {
-                return mService.dialMemory(device, location);
-            } catch (RemoteException e) {
-                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
-            }
-        }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
+        return null;
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index c73bc3c..420c079f 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 
 import java.util.UUID;
 
@@ -70,6 +71,7 @@
     private boolean mMultiParty;
     private final boolean mOutgoing;
     private final UUID mUUID;
+    private final long mCreationElapsedMilli;
 
     /**
      * Creates BluetoothHeadsetClientCall instance.
@@ -88,6 +90,7 @@
         mNumber = number != null ? number : "";
         mMultiParty = multiParty;
         mOutgoing = outgoing;
+        mCreationElapsedMilli = SystemClock.elapsedRealtime();
     }
 
     /**
@@ -171,6 +174,15 @@
     }
 
     /**
+     * Gets call's creation time in millis since epoch.
+     *
+     * @return long representing the creation time.
+     */
+    public long getCreationElapsedMilli() {
+        return mCreationElapsedMilli;
+    }
+
+    /**
      * Checks if call is an active call in a conference mode (aka multi party).
      *
      * @return <code>true</code> if call is a multi party call,
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
new file mode 100644
index 0000000..4252482
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 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 android.bluetooth;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the APIs to control the Bluetooth MAP MCE Profile.
+ *
+ * @hide
+ */
+public final class BluetoothMapClient implements BluetoothProfile {
+
+    private static final String TAG = "BluetoothMapClient";
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
+    public static final String ACTION_MESSAGE_RECEIVED =
+            "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
+    /* Actions to be used for pending intents */
+    public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
+            "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
+    public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
+            "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+
+    private IBluetoothMapClient mService;
+    private final Context mContext;
+    private ServiceListener mServiceListener;
+    private BluetoothAdapter mAdapter;
+
+    /** There was an error trying to obtain the state */
+    public static final int STATE_ERROR = -1;
+
+    public static final int RESULT_FAILURE = 0;
+    public static final int RESULT_SUCCESS = 1;
+    /** Connection canceled before completion. */
+    public static final int RESULT_CANCELED = 2;
+
+    final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                public void onBluetoothStateChange(boolean up) {
+                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                    if (!up) {
+                        if (VDBG) Log.d(TAG, "Unbinding service...");
+                        synchronized (mConnection) {
+                            try {
+                                mService = null;
+                                mContext.unbindService(mConnection);
+                            } catch (Exception re) {
+                                Log.e(TAG, "", re);
+                            }
+                        }
+                    } else {
+                        synchronized (mConnection) {
+                            try {
+                                if (mService == null) {
+                                    if (VDBG) Log.d(TAG, "Binding service...");
+                                    doBind();
+                                }
+                            } catch (Exception re) {
+                                Log.e(TAG, "", re);
+                            }
+                        }
+                    }
+                }
+            };
+
+    /**
+     * Create a BluetoothMapClient proxy object.
+     */
+    /*package*/ BluetoothMapClient(Context context, ServiceListener l) {
+        if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
+        mContext = context;
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothMapClient.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Close the connection to the backing service.
+     * Other public functions of BluetoothMap will return default error
+     * results once close() has been called. Multiple invocations of close()
+     * are ok.
+     */
+    public void close() {
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (Exception e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        synchronized (mConnection) {
+            if (mService != null) {
+                try {
+                    mService = null;
+                    mContext.unbindService(mConnection);
+                } catch (Exception re) {
+                    Log.e(TAG, "", re);
+                }
+            }
+        }
+        mServiceListener = null;
+    }
+
+    /**
+     * Returns true if the specified Bluetooth device is connected.
+     * Returns false if not connected, or if this proxy object is not
+     * currently connected to the Map service.
+     */
+    public boolean isConnected(BluetoothDevice device) {
+        if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
+        if (mService != null) {
+            try {
+                return mService.isConnected(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
+    /**
+     * Initiate connection. Initiation of outgoing connections is not
+     * supported for MAP server.
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
+        if (mService != null) {
+            try {
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
+    /**
+     * Initiate disconnect.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on error, true otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the list of connected devices. Currently at most one.
+     *
+     * @return list of connected devices
+     */
+    @Override
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) Log.d(TAG, "getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<>();
+    }
+
+    /**
+     * Get the list of devices matching specified states. Currently at most one.
+     *
+     * @return list of matching devices
+     */
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<>();
+    }
+
+    /**
+     * Get connection state of device
+     *
+     * @return device connection state
+     */
+    @Override
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.  Priority can be one of {@link #PRIORITY_ON} or
+     * {@link #PRIORITY_OFF},
+     *
+     * @param device Paired bluetooth device
+     * @return true if priority is set, false on error
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                    priority != BluetoothProfile.PRIORITY_ON) {
+                return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    /**
+     * Send a message.
+     *
+     * Send an SMS message to either the contacts primary number or the telephone number specified.
+     *
+     * @param device          Bluetooth device
+     * @param contacts        Uri[] of the contacts
+     * @param message         Message to be sent
+     * @param sentIntent      intent issued when message is sent
+     * @param deliveredIntent intent issued when message is delivered
+     * @return true if the message is enqueued, false on error
+     */
+    public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
+            PendingIntent sentIntent, PendingIntent deliveredIntent) {
+        if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
+        if (mService != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get unread messages.  Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
+     *
+     * @param device Bluetooth device
+     * @return true if the message is enqueued, false on error
+     */
+    public boolean getUnreadMessages(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
+        if (mService != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return mService.getUnreadMessages(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            mService = IBluetoothMapClient.Stub.asInterface(service);
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT,
+                    BluetoothMapClient.this);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        if (DBG) Log.d(TAG, "Bluetooth is Not enabled");
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+        if (device == null) return false;
+
+        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+        return false;
+    }
+
+
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index eee66d1..f363607 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2014 The Android Open Source Project
+ * Copyright (C) 2010-2016 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.
@@ -137,6 +137,19 @@
     public static final int PBAP_CLIENT = 17;
 
     /**
+     * MAP Messaging Client Equipment (MCE)
+     * @hide
+     */
+    public static final int MAP_CLIENT = 18;
+
+    /**
+     * Max profile ID. This value should be updated whenever a new profile is added to match
+     * the largest value assigned to a profile.
+     * @hide
+     */
+    public static final int MAX_PROFILE_ID = 17;
+
+    /**
      * Default priority for devices that we try to auto-connect to and
      * and allow incoming connections for the profile
      * @hide
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 2ded4c8..243579a 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -275,6 +275,48 @@
     }
 
     /**
+     * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID,
+     * Note returned value is little endian (Bluetooth).
+     *
+     * @param uuid uuid to parse.
+     * @return shortest representation of {@code uuid} as bytes.
+     * @throws IllegalArgumentException If the {@code uuid} is null.
+     */
+    public static byte[] uuidToBytes(ParcelUuid uuid) {
+        if (uuid == null) {
+            throw new IllegalArgumentException("uuid cannot be null");
+        }
+
+        if (is16BitUuid(uuid)) {
+            byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
+            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
+            uuidBytes[0] = (byte)(uuidVal & 0xFF);
+            uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8);
+            return uuidBytes;
+        }
+
+        if (is32BitUuid(uuid)) {
+            byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
+            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
+            uuidBytes[0] = (byte)(uuidVal & 0xFF);
+            uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8);
+            uuidBytes[2] = (byte)((uuidVal & 0xFF0000) >> 16);
+            uuidBytes[3] = (byte)((uuidVal & 0xFF000000) >> 24);
+            return uuidBytes;
+        }
+
+        // Construct a 128 bit UUID.
+        long msb = uuid.getUuid().getMostSignificantBits();
+        long lsb = uuid.getUuid().getLeastSignificantBits();
+
+        byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
+        ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
+        buf.putLong(8, msb);
+        buf.putLong(0, lsb);
+        return uuidBytes;
+    }
+
+    /**
      * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
      *
      * @param parcelUuid
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 8c98536..7c5458b 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -63,6 +63,7 @@
     boolean removeBond(in BluetoothDevice device);
     int getBondState(in BluetoothDevice device);
     boolean isBondingInitiatedLocally(in BluetoothDevice device);
+    long getSupportedProfiles();
     int getConnectionState(in BluetoothDevice device);
 
     String getRemoteName(in BluetoothDevice device);
@@ -99,7 +100,6 @@
     boolean factoryReset();
 
     boolean isMultiAdvertisementSupported();
-    boolean isPeripheralModeSupported();
     boolean isOffloadedFilteringSupported();
     boolean isOffloadedScanBatchingSupported();
     boolean isActivityAndEnergyReportingSupported();
diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
index f1288d0..cfa11ca 100644
--- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
+++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
@@ -30,10 +30,7 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
     BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device);
-    MediaMetadata getMetadata(in BluetoothDevice device);
-    PlaybackState getPlaybackState(in BluetoothDevice device);
     boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting);
     void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState);
 }
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index f4ebcaf..aa2291e 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -29,6 +29,7 @@
 import android.bluetooth.IBluetoothGattCallback;
 import android.bluetooth.IBluetoothGattServerCallback;
 import android.bluetooth.le.IAdvertiserCallback;
+import android.bluetooth.le.IScannerCallback;
 
 /**
  * API for interacting with BLE / GATT
@@ -37,11 +38,12 @@
 interface IBluetoothGatt {
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
 
-    void startScan(in int appIf, in boolean isServer, in ScanSettings settings,
-                   in List<ScanFilter> filters, in WorkSource workSource, in List scanStorages,
-                   in String callingPackage);
-    void stopScan(in int appIf, in boolean isServer);
-    void flushPendingBatchResults(in int appIf, in boolean isServer);
+    void registerScanner(in IScannerCallback callback);
+    void unregisterScanner(in int scannerId);
+    void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
+                   in WorkSource workSource, in List scanStorages, in String callingPackage);
+    void stopScan(in int scannerId);
+    void flushPendingBatchResults(in int scannerId);
 
     void registerAdvertiser(in IAdvertiserCallback callback);
     void unregisterAdvertiser(in int advertiserId);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index efda08e..72cb618 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -17,8 +17,6 @@
 
 import android.os.ParcelUuid;
 import android.bluetooth.BluetoothGattService;
-import android.bluetooth.le.AdvertiseSettings;
-import android.bluetooth.le.ScanResult;
 
 /**
  * Callback definitions for interacting with BLE / GATT
@@ -28,8 +26,6 @@
     void onClientRegistered(in int status, in int clientIf);
     void onClientConnectionState(in int status, in int clientIf,
                                  in boolean connected, in String address);
-    void onScanResult(in ScanResult scanResult);
-    void onBatchScanResults(in List<ScanResult> batchResults);
     void onSearchComplete(in String address, in List<BluetoothGattService> services, in int status);
     void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value);
     void onCharacteristicWrite(in String address, in int status, in int handle);
@@ -38,7 +34,5 @@
     void onDescriptorWrite(in String address, in int status, in int handle);
     void onNotify(in String address, in int handle, in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
-    void onScanManagerErrorCallback(in int errorCode);
     void onConfigureMTU(in String address, in int mtu, in int status);
-    void onFoundOrLost(in boolean onFound, in ScanResult scanResult);
 }
diff --git a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
index 0bcb07b..1a924fb 100644
--- a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
@@ -23,7 +23,6 @@
  */
 oneway interface IBluetoothGattServerCallback {
     void onServerRegistered(in int status, in int serverIf);
-    void onScanResult(in String address, in int rssi, in byte[] advData);
     void onServerConnectionState(in int status, in int serverIf,
                                  in boolean connected, in String address);
     void onServiceAdded(in int status, in BluetoothGattService service);
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
index 79ae4e4..a351bd2 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -47,14 +47,12 @@
     boolean acceptCall(in BluetoothDevice device, int flag);
     boolean holdCall(in BluetoothDevice device);
     boolean rejectCall(in BluetoothDevice device);
-    boolean terminateCall(in BluetoothDevice device, int index);
+    boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call);
 
     boolean enterPrivateMode(in BluetoothDevice device, int index);
     boolean explicitCallTransfer(in BluetoothDevice device);
 
-    boolean redial(in BluetoothDevice device);
-    boolean dial(in BluetoothDevice device, String number);
-    boolean dialMemory(in BluetoothDevice device, int location);
+    BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number);
 
     boolean sendDTMF(in BluetoothDevice device, byte code);
     boolean getLastVoiceTagNumber(in BluetoothDevice device);
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 0b81ee8..f39ca8e 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -34,9 +34,9 @@
     void registerStateChangeCallback(in IBluetoothStateChangeCallback callback);
     void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
     boolean isEnabled();
-    boolean enable();
+    boolean enable(String packageName);
     boolean enableNoAutoConnect();
-    boolean disable(boolean persist);
+    boolean disable(String packageName, boolean persist);
     IBluetoothGatt getBluetoothGatt();
 
     boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
@@ -49,3 +49,4 @@
     int updateBleAppCount(IBinder b, boolean enable);
     boolean isBleAppPresent();
 }
+
diff --git a/core/java/android/bluetooth/IBluetoothMapClient.aidl b/core/java/android/bluetooth/IBluetoothMapClient.aidl
new file mode 100644
index 0000000..df45af9
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothMapClient.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 android.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
+
+/**
+ * System private API for Bluetooth MAP MCE service
+ *
+ * {@hide}
+ */
+interface IBluetoothMapClient {
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    boolean isConnected(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device,in int priority);
+    int getPriority(in BluetoothDevice device);
+    boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in  String message,
+        in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+    boolean getUnreadMessages(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index f53ca94..9e87230 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -30,10 +30,24 @@
  * @hide
  */
 public class OobData implements Parcelable {
+    private byte[] leBluetoothDeviceAddress;
     private byte[] securityManagerTk;
     private byte[] leSecureConnectionsConfirmation;
     private byte[] leSecureConnectionsRandom;
 
+    public byte[] getLeBluetoothDeviceAddress() {
+        return leBluetoothDeviceAddress;
+    }
+
+    /**
+     * Sets the LE Bluetooth Device Address value to be used during LE pairing.
+     * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
+     * a detailed description.
+     */
+    public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
+        this.leBluetoothDeviceAddress = leBluetoothDeviceAddress;
+    }
+
     public byte[] getSecurityManagerTk() {
         return securityManagerTk;
     }
@@ -66,6 +80,7 @@
     public OobData() { }
 
     private OobData(Parcel in) {
+        leBluetoothDeviceAddress = in.createByteArray();
         securityManagerTk = in.createByteArray();
         leSecureConnectionsConfirmation = in.createByteArray();
         leSecureConnectionsRandom = in.createByteArray();
@@ -77,6 +92,7 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(leBluetoothDeviceAddress);
         out.writeByteArray(securityManagerTk);
         out.writeByteArray(leSecureConnectionsConfirmation);
         out.writeByteArray(leSecureConnectionsRandom);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 048f791..5d27662 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -18,7 +18,6 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCallbackWrapper;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManager;
@@ -112,12 +111,6 @@
             if (callback == null) {
                 throw new IllegalArgumentException("callback cannot be null");
             }
-            if (!mBluetoothAdapter.isMultipleAdvertisementSupported() &&
-                    !mBluetoothAdapter.isPeripheralModeSupported()) {
-                postStartFailure(callback,
-                        AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);
-                return;
-            }
             boolean isConnectable = settings.isConnectable();
             if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
                     totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {
@@ -237,9 +230,9 @@
         private final AdvertiseSettings mSettings;
         private final IBluetoothGatt mBluetoothGatt;
 
-        // mAdvertiserId 0: not registered
-        // -1: advertise stopped or registration timeout
-        // >0: registered and advertising started
+        // mAdvertiserId -1: not registered
+        // -2: advertise stopped or registration timeout
+        // >=0: registered and advertising started
         private int mAdvertiserId;
         private boolean mIsAdvertising = false;
 
@@ -252,12 +245,12 @@
             mScanResponse = scanResponse;
             mSettings = settings;
             mBluetoothGatt = bluetoothGatt;
-            mAdvertiserId = 0;
+            mAdvertiserId = -1;
         }
 
         public void startRegisteration() {
             synchronized (this) {
-                if (mAdvertiserId == -1) return;
+                if (mAdvertiserId == -2) return;
 
                 try {
                     mBluetoothGatt.registerAdvertiser(this);
@@ -265,13 +258,13 @@
                 } catch (InterruptedException | RemoteException e) {
                     Log.e(TAG, "Failed to start registeration", e);
                 }
-                if (mAdvertiserId > 0 && mIsAdvertising) {
+                if (mAdvertiserId >= 0 && mIsAdvertising) {
                     mLeAdvertisers.put(mAdvertiseCallback, this);
-                } else if (mAdvertiserId <= 0) {
+                } else if (mAdvertiserId < 0) {
 
                     // Registration timeout, reset mClientIf to -1 so no subsequent operations can
                     // proceed.
-                    if (mAdvertiserId == 0) mAdvertiserId = -1;
+                    if (mAdvertiserId == 0) mAdvertiserId = -2;
                     // Post internal error if registration failed.
                     postStartFailure(mAdvertiseCallback,
                             AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
@@ -279,7 +272,7 @@
                     // Unregister application if it's already registered but advertise failed.
                     try {
                         mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
-                        mAdvertiserId = -1;
+                        mAdvertiserId = -2;
                     } catch (RemoteException e) {
                         Log.e(TAG, "remote exception when unregistering", e);
                     }
@@ -313,7 +306,7 @@
             synchronized (this) {
                 if (status == BluetoothGatt.GATT_SUCCESS) {
                     try {
-                        if (mAdvertiserId == -1) {
+                        if (mAdvertiserId == -2) {
                             // Registration succeeds after timeout, unregister advertiser.
                             mBluetoothGatt.unregisterAdvertiser(advertiserId);
                         } else {
@@ -327,7 +320,7 @@
                     }
                 }
                 // Registration failed.
-                mAdvertiserId = -1;
+                mAdvertiserId = -2;
                 notifyAll();
             }
         }
@@ -349,7 +342,7 @@
                     // unregister advertiser for stop.
                     try {
                         mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
-                        mAdvertiserId = -1;
+                        mAdvertiserId = -2;
                         mIsAdvertising = false;
                         mLeAdvertisers.remove(mAdvertiseCallback);
                     } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 5715ff8..b63c614 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -22,9 +22,9 @@
 import android.app.ActivityThread;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCallbackWrapper;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManager;
+import android.bluetooth.le.IScannerCallback;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -267,7 +267,7 @@
     /**
      * Bluetooth GATT interface callbacks
      */
-    private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper {
+    private class BleScanCallbackWrapper extends IScannerCallback.Stub {
         private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
 
         private final ScanCallback mScanCallback;
@@ -280,7 +280,7 @@
         // mLeHandle 0: not registered
         // -1: scan stopped or registration failed
         // > 0: registered and scan started
-        private int mClientIf;
+        private int mScannerId;
 
         public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
                 List<ScanFilter> filters, ScanSettings settings,
@@ -291,28 +291,27 @@
             mSettings = settings;
             mWorkSource = workSource;
             mScanCallback = scanCallback;
-            mClientIf = 0;
+            mScannerId = 0;
             mResultStorages = resultStorages;
         }
 
         public void startRegisteration() {
             synchronized (this) {
                 // Scan stopped.
-                if (mClientIf == -1) return;
+                if (mScannerId == -1) return;
                 try {
-                    UUID uuid = UUID.randomUUID();
-                    mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);
+                    mBluetoothGatt.registerScanner(this);
                     wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
                 } catch (InterruptedException | RemoteException e) {
                     Log.e(TAG, "application registeration exception", e);
                     postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
                 }
-                if (mClientIf > 0) {
+                if (mScannerId > 0) {
                     mLeScanClients.put(mScanCallback, this);
                 } else {
-                    // Registration timed out or got exception, reset clientIf to -1 so no
+                    // Registration timed out or got exception, reset scannerId to -1 so no
                     // subsequent operations can proceed.
-                    if (mClientIf == 0) mClientIf = -1;
+                    if (mScannerId == 0) mScannerId = -1;
                     postCallbackError(mScanCallback,
                             ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
                 }
@@ -321,28 +320,28 @@
 
         public void stopLeScan() {
             synchronized (this) {
-                if (mClientIf <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
+                if (mScannerId <= 0) {
+                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
                     return;
                 }
                 try {
-                    mBluetoothGatt.stopScan(mClientIf, false);
-                    mBluetoothGatt.unregisterClient(mClientIf);
+                    mBluetoothGatt.stopScan(mScannerId);
+                    mBluetoothGatt.unregisterScanner(mScannerId);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to stop scan and unregister", e);
                 }
-                mClientIf = -1;
+                mScannerId = -1;
             }
         }
 
         void flushPendingBatchResults() {
             synchronized (this) {
-                if (mClientIf <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
+                if (mScannerId <= 0) {
+                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
                     return;
                 }
                 try {
-                    mBluetoothGatt.flushPendingBatchResults(mClientIf, false);
+                    mBluetoothGatt.flushPendingBatchResults(mScannerId);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to get pending scan results", e);
                 }
@@ -353,28 +352,28 @@
          * Application interface registered - app is ready to go
          */
         @Override
-        public void onClientRegistered(int status, int clientIf) {
-            Log.d(TAG, "onClientRegistered() - status=" + status +
-                    " clientIf=" + clientIf + " mClientIf=" + mClientIf);
+        public void onScannerRegistered(int status, int scannerId) {
+            Log.d(TAG, "onScannerRegistered() - status=" + status +
+                    " scannerId=" + scannerId + " mScannerId=" + mScannerId);
             synchronized (this) {
                 if (status == BluetoothGatt.GATT_SUCCESS) {
                     try {
-                        if (mClientIf == -1) {
+                        if (mScannerId == -1) {
                             // Registration succeeds after timeout, unregister client.
-                            mBluetoothGatt.unregisterClient(clientIf);
+                            mBluetoothGatt.unregisterClient(scannerId);
                         } else {
-                            mClientIf = clientIf;
-                            mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters,
+                            mScannerId = scannerId;
+                            mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
                                     mWorkSource, mResultStorages,
                                     ActivityThread.currentOpPackageName());
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "fail to start le scan: " + e);
-                        mClientIf = -1;
+                        mScannerId = -1;
                     }
                 } else {
                     // registration failed
-                    mClientIf = -1;
+                    mScannerId = -1;
                 }
                 notifyAll();
             }
@@ -391,7 +390,7 @@
 
             // Check null in case the scan has been stopped
             synchronized (this) {
-                if (mClientIf <= 0) return;
+                if (mScannerId <= 0) return;
             }
             Handler handler = new Handler(Looper.getMainLooper());
             handler.post(new Runnable() {
@@ -423,7 +422,7 @@
 
             // Check null in case the scan has been stopped
             synchronized (this) {
-                if (mClientIf <= 0)
+                if (mScannerId <= 0)
                     return;
             }
             Handler handler = new Handler(Looper.getMainLooper());
@@ -447,7 +446,7 @@
                 Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
             }
             synchronized (this) {
-                if (mClientIf <= 0)
+                if (mScannerId <= 0)
                     return;
             }
             postCallbackError(mScanCallback, errorCode);
diff --git a/core/java/android/bluetooth/le/IScannerCallback.aidl b/core/java/android/bluetooth/le/IScannerCallback.aidl
new file mode 100644
index 0000000..8cbbaef
--- /dev/null
+++ b/core/java/android/bluetooth/le/IScannerCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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 android.bluetooth.le;
+
+import android.bluetooth.le.ScanResult;
+
+/**
+ * Callback definitions for interacting with Advertiser
+ * @hide
+ */
+oneway interface IScannerCallback {
+    void onScannerRegistered(in int status, in int scannerId);
+
+    void onScanResult(in ScanResult scanResult);
+    void onBatchScanResults(in List<ScanResult> batchResults);
+    void onFoundOrLost(in boolean onFound, in ScanResult scanResult);
+    void onScanManagerErrorCallback(in int errorCode);
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fee927c..b79c72e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2664,7 +2664,7 @@
             NETWORK_STATS_SERVICE,
             //@hide: NETWORK_POLICY_SERVICE,
             WIFI_SERVICE,
-            WIFI_NAN_SERVICE,
+            WIFI_AWARE_SERVICE,
             WIFI_P2P_SERVICE,
             WIFI_SCANNING_SERVICE,
             //@hide: WIFI_RTT_SERVICE,
@@ -3134,14 +3134,14 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.net.wifi.nan.WifiNanManager} for handling management of
-     * Wi-Fi NAN.
+     * {@link android.net.wifi.aware.WifiAwareManager} for handling management of
+     * Wi-Fi Aware.
      *
      * @see #getSystemService
-     * @see android.net.wifi.nan.WifiNanManager
-     * @hide PROPOSED_NAN_API
+     * @see android.net.wifi.aware.WifiAwareManager
+     * @hide PROPOSED_AWARE_API
      */
-    public static final String WIFI_NAN_SERVICE = "wifinan";
+    public static final String WIFI_AWARE_SERVICE = "wifiaware";
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 089a420..8122940 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2028,12 +2028,12 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device supports Wi-Fi NAN.
+     * {@link #hasSystemFeature}: The device supports Wi-Fi Aware.
      *
-     * @hide PROPOSED_NAN_API
+     * @hide PROPOSED_AWARE_API
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_WIFI_NAN = "android.hardware.wifi.nan";
+    public static final String FEATURE_WIFI_AWARE = "android.hardware.wifi.aware";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index e0d3905..a05a8ec 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -248,7 +248,8 @@
         return program;
     }
 
-    private void drawFrame(SurfaceTexture st, int width, int height, int flipType) {
+    private void drawFrame(SurfaceTexture st, int width, int height, int flipType)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
         checkGlError("onDrawFrame start");
         st.getTransformMatrix(mSTMatrix);
 
@@ -343,7 +344,7 @@
                 /*offset*/ 0);
 
         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
-        checkGlError("glDrawArrays");
+        checkGlDrawError("glDrawArrays");
     }
 
     /**
@@ -548,7 +549,29 @@
     private void checkGlError(String msg) {
         int error;
         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
-            throw new IllegalStateException(msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+            throw new IllegalStateException(
+                    msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+        }
+    }
+
+    private void checkGlDrawError(String msg)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
+        int error;
+        boolean surfaceAbandoned = false;
+        boolean glError = false;
+        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+            if (error == GLES20.GL_OUT_OF_MEMORY) {
+                surfaceAbandoned = true;
+            } else {
+                glError = true;
+            }
+        }
+        if (glError) {
+            throw new IllegalStateException(
+                    msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+        }
+        if (surfaceAbandoned) {
+            throw new LegacyExceptionUtils.BufferQueueAbandonedException();
         }
     }
 
@@ -759,9 +782,14 @@
             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
                 // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
-                drawFrame(mSurfaceTexture, holder.width, holder.height,
-                        (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
-                                FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
+                try {
+                    drawFrame(mSurfaceTexture, holder.width, holder.height,
+                            (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
+                                    FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
+                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                    // Should never hit this.
+                    throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
+                }
                 mPBufferPixels.clear();
                 GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
                         GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 6e4c9de..00b0bff 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -87,15 +87,13 @@
     /* Returns true if the specified USB function is enabled. */
     boolean isFunctionEnabled(String function);
 
-    /* Sets the current USB function. */
-    void setCurrentFunction(String function);
-
-    /* Sets whether USB data (for example, MTP exposed pictures) should be made
-     * available on the USB connection. Unlocking data should only be done with
-     * user involvement, since exposing pictures or other data could leak sensitive
-     * user information.
+    /* Sets the current USB function as well as whether USB data
+     * (for example, MTP exposed pictures) should be made available
+     * on the USB connection. Unlocking data should only be done with
+     * user involvement, since exposing pictures or other data could
+     * leak sensitive user information.
      */
-    void setUsbDataUnlocked(boolean unlock);
+    void setCurrentFunction(String function, boolean usbDataUnlocked);
 
     /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
      * the public key to list of host keys that the user has approved.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f9a7d19..6341cbc 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -509,33 +509,23 @@
      * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
      * or {@link #USB_FUNCTION_RNDIS}.
      * </p><p>
+     * Also sets whether USB data (for example, MTP exposed pictures) should be made available
+     * on the USB connection when in device mode. Unlocking usb data should only be done with
+     * user involvement, since exposing pictures or other data could leak sensitive
+     * user information.
+     * </p><p>
      * Note: This function is asynchronous and may fail silently without applying
      * the requested changes.
      * </p>
      *
      * @param function name of the USB function, or null to restore the default function
+     * @param usbDataUnlocked whether user data is accessible
      *
      * {@hide}
      */
-    public void setCurrentFunction(String function) {
+    public void setCurrentFunction(String function, boolean usbDataUnlocked) {
         try {
-            mService.setCurrentFunction(function);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Sets whether USB data (for example, MTP exposed pictures) should be made available
-     * on the USB connection when in device mode. Unlocking usb data should only be done with
-     * user involvement, since exposing pictures or other data could leak sensitive
-     * user information.
-     *
-     * {@hide}
-     */
-    public void setUsbDataUnlocked(boolean unlocked) {
-        try {
-            mService.setUsbDataUnlocked(unlocked);
+            mService.setCurrentFunction(function, usbDataUnlocked);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index ea53a71..34cde08 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -113,7 +113,7 @@
      */
     public boolean isActive() {
         try {
-            if (hasFlag(FLAG_UP)) {
+            if (isUp()) {
                 for (byte b : mAddr.getAddress().getAddress()) {
                     if (b != 0) return true;
                 }
@@ -124,6 +124,10 @@
         return false;
     }
 
+    public boolean isUp() {
+        return hasFlag(FLAG_UP);
+    }
+
     /** {@inheritDoc} */
     public int describeContents() {
         return 0;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ebb9601..cbb6dfe 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -406,14 +406,14 @@
     public static final int TRANSPORT_VPN = 4;
 
     /**
-     * Indicates this network uses a Wi-Fi NAN transport.
+     * Indicates this network uses a Wi-Fi Aware transport.
      *
-     * @hide PROPOSED_NAN_API
+     * @hide PROPOSED_AWARE_API
      */
-    public static final int TRANSPORT_WIFI_NAN = 5;
+    public static final int TRANSPORT_WIFI_AWARE = 5;
 
     private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
-    private static final int MAX_TRANSPORT = TRANSPORT_WIFI_NAN;
+    private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
 
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
@@ -869,7 +869,7 @@
                 case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
                 case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
                 case TRANSPORT_VPN:         transports += "VPN"; break;
-                case TRANSPORT_WIFI_NAN:    transports += "WIFI_NAN"; break;
+                case TRANSPORT_WIFI_AWARE:  transports += "WIFI_AWARE"; break;
             }
             if (++i < types.length) transports += "|";
         }
diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java
index 58acbcf..c7bf0fd 100644
--- a/core/java/android/os/CountDownTimer.java
+++ b/core/java/android/os/CountDownTimer.java
@@ -125,19 +125,28 @@
 
                 if (millisLeft <= 0) {
                     onFinish();
-                } else if (millisLeft < mCountdownInterval) {
-                    // no tick, just delay until done
-                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                 } else {
                     long lastTickStart = SystemClock.elapsedRealtime();
                     onTick(millisLeft);
 
                     // take into account user's onTick taking time to execute
-                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
+                    long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
+                    long delay;
 
-                    // special case: user's onTick took more than interval to
-                    // complete, skip to next interval
-                    while (delay < 0) delay += mCountdownInterval;
+                    if (millisLeft < mCountdownInterval) {
+                        // just delay until done
+                        delay = millisLeft - lastTickDuration;
+
+                        // special case: user's onTick took more than interval to
+                        // complete, trigger onFinish without delay
+                        if (delay < 0) delay = 0;
+                    } else {
+                        delay = mCountdownInterval - lastTickDuration;
+
+                        // special case: user's onTick took more than interval to
+                        // complete, skip to next interval
+                        while (delay < 0) delay += mCountdownInterval;
+                    }
 
                     sendMessageDelayed(obtainMessage(MSG), delay);
                 }
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 5ff79f7..481b2dc 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import java.util.ArrayList;
 import libcore.util.NativeAllocationRegistry;
 
 /** @hide */
@@ -38,8 +39,13 @@
     public abstract void onTransact(
             int code, HwParcel request, HwParcel reply, int flags);
 
-    public native final void registerService(String serviceName);
-    public static native final IHwBinder getService(String serviceName);
+    public native final void registerService(
+            ArrayList<String> interfaceChain,
+            String serviceName);
+
+    public static native final IHwBinder getService(
+            String iface,
+            String serviceName);
 
     // Returns address of the "freeFunction".
     private static native final long native_init();
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 180e8f4..c7612d1 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 import libcore.util.NativeAllocationRegistry;
 
 /** @hide */
@@ -53,14 +56,88 @@
     public native final void writeDouble(double val);
     public native final void writeString(String val);
 
-    public native final void writeBoolVector(boolean[] val);
-    public native final void writeInt8Vector(byte[] val);
-    public native final void writeInt16Vector(short[] val);
-    public native final void writeInt32Vector(int[] val);
-    public native final void writeInt64Vector(long[] val);
-    public native final void writeFloatVector(float[] val);
-    public native final void writeDoubleVector(double[] val);
-    public native final void writeStringVector(String[] val);
+    private native final void writeBoolVector(boolean[] val);
+    private native final void writeInt8Vector(byte[] val);
+    private native final void writeInt16Vector(short[] val);
+    private native final void writeInt32Vector(int[] val);
+    private native final void writeInt64Vector(long[] val);
+    private native final void writeFloatVector(float[] val);
+    private native final void writeDoubleVector(double[] val);
+    private native final void writeStringVector(String[] val);
+
+    public final void writeBoolVector(ArrayList<Boolean> val) {
+        final int n = val.size();
+        boolean[] array = new boolean[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeBoolVector(array);
+    }
+
+    public final void writeInt8Vector(ArrayList<Byte> val) {
+        final int n = val.size();
+        byte[] array = new byte[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt8Vector(array);
+    }
+
+    public final void writeInt16Vector(ArrayList<Short> val) {
+        final int n = val.size();
+        short[] array = new short[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt16Vector(array);
+    }
+
+    public final void writeInt32Vector(ArrayList<Integer> val) {
+        final int n = val.size();
+        int[] array = new int[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt32Vector(array);
+    }
+
+    public final void writeInt64Vector(ArrayList<Long> val) {
+        final int n = val.size();
+        long[] array = new long[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt64Vector(array);
+    }
+
+    public final void writeFloatVector(ArrayList<Float> val) {
+        final int n = val.size();
+        float[] array = new float[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeFloatVector(array);
+    }
+
+    public final void writeDoubleVector(ArrayList<Double> val) {
+        final int n = val.size();
+        double[] array = new double[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeDoubleVector(array);
+    }
+
+    public final void writeStringVector(ArrayList<String> val) {
+        writeStringVector(val.toArray(new String[val.size()]));
+    }
 
     public native final void writeStrongBinder(IHwBinder binder);
 
@@ -74,14 +151,60 @@
     public native final double readDouble();
     public native final String readString();
 
-    public native final boolean[] readBoolVector();
-    public native final byte[] readInt8Vector();
-    public native final short[] readInt16Vector();
-    public native final int[] readInt32Vector();
-    public native final long[] readInt64Vector();
-    public native final float[] readFloatVector();
-    public native final double[] readDoubleVector();
-    public native final String[] readStringVector();
+    private native final boolean[] readBoolVectorAsArray();
+    private native final byte[] readInt8VectorAsArray();
+    private native final short[] readInt16VectorAsArray();
+    private native final int[] readInt32VectorAsArray();
+    private native final long[] readInt64VectorAsArray();
+    private native final float[] readFloatVectorAsArray();
+    private native final double[] readDoubleVectorAsArray();
+    private native final String[] readStringVectorAsArray();
+
+    public final ArrayList<Boolean> readBoolVector() {
+        Boolean[] array = HwBlob.wrapArray(readBoolVectorAsArray());
+
+        return new ArrayList<Boolean>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Byte> readInt8Vector() {
+        Byte[] array = HwBlob.wrapArray(readInt8VectorAsArray());
+
+        return new ArrayList<Byte>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Short> readInt16Vector() {
+        Short[] array = HwBlob.wrapArray(readInt16VectorAsArray());
+
+        return new ArrayList<Short>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Integer> readInt32Vector() {
+        Integer[] array = HwBlob.wrapArray(readInt32VectorAsArray());
+
+        return new ArrayList<Integer>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Long> readInt64Vector() {
+        Long[] array = HwBlob.wrapArray(readInt64VectorAsArray());
+
+        return new ArrayList<Long>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Float> readFloatVector() {
+        Float[] array = HwBlob.wrapArray(readFloatVectorAsArray());
+
+        return new ArrayList<Float>(Arrays.asList(array));
+    }
+
+    public final ArrayList<Double> readDoubleVector() {
+        Double[] array = HwBlob.wrapArray(readDoubleVectorAsArray());
+
+        return new ArrayList<Double>(Arrays.asList(array));
+    }
+
+    public final ArrayList<String> readStringVector() {
+        return new ArrayList<String>(Arrays.asList(readStringVectorAsArray()));
+    }
 
     public native final IHwBinder readStrongBinder();
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6aa9fac..3fccdb0 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -18,6 +18,7 @@
 
 import android.system.Os;
 import android.util.Log;
+import android.webkit.WebViewZygote;
 import dalvik.system.VMRuntime;
 
 /**
@@ -131,6 +132,12 @@
     public static final int CAMERASERVER_UID = 1047;
 
     /**
+     * Defines the UID/GID for the WebView zygote process.
+     * @hide
+     */
+    public static final int WEBVIEW_ZYGOTE_UID = 1051;
+
+    /**
      * Defines the start of a range of UIDs (and GIDs), going from this
      * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
      * to applications.
@@ -417,6 +424,22 @@
                     abi, instructionSet, appDataDir, zygoteArgs);
     }
 
+    /** @hide */
+    public static final ProcessStartResult startWebView(final String processClass,
+                                  final String niceName,
+                                  int uid, int gid, int[] gids,
+                                  int debugFlags, int mountExternal,
+                                  int targetSdkVersion,
+                                  String seInfo,
+                                  String abi,
+                                  String instructionSet,
+                                  String appDataDir,
+                                  String[] zygoteArgs) {
+        return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
+                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, zygoteArgs);
+    }
+
     /**
      * Returns elapsed milliseconds of the time this process has run.
      * @return  Returns the number of milliseconds this process has return.
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index fc804e5..0b4c4c1 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -274,7 +274,7 @@
     /**
      * Implement parsing and execution of a command.  If it isn't a command you understand,
      * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
-     * User {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
+     * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
      * to process additional command line arguments.  Command output can be written to
      * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
      *
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index d7a7296..d5206d4 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -19,7 +19,9 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.Zygote;
+import com.android.internal.util.Preconditions;
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.IOException;
@@ -110,7 +112,8 @@
             }
 
             String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
-            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+            Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
+                    + abiListString);
 
             return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                     Arrays.asList(abiListString.split(",")));
@@ -136,6 +139,13 @@
     }
 
     /**
+     * Lock object to protect access to the two ZygoteStates below. This lock must be
+     * acquired while communicating over the ZygoteState's socket, to prevent
+     * interleaved access.
+     */
+    private final Object mLock = new Object();
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -207,6 +217,7 @@
      *
      * @throws ZygoteStartFailedEx if the query failed.
      */
+    @GuardedBy("mLock")
     private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
             throws IOException {
         // Each query starts with the argument count (1 in this case)
@@ -233,6 +244,7 @@
      *
      * @throws ZygoteStartFailedEx if process start failed for any reason
      */
+    @GuardedBy("mLock")
     private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
             ZygoteState zygoteState, ArrayList<String> args)
             throws ZygoteStartFailedEx {
@@ -311,90 +323,90 @@
                                                       String appDataDir,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
-        synchronized(Process.class) {
-            ArrayList<String> argsForZygote = new ArrayList<String>();
+        ArrayList<String> argsForZygote = new ArrayList<String>();
 
-            // --runtime-args, --setuid=, --setgid=,
-            // and --setgroups= must go first
-            argsForZygote.add("--runtime-args");
-            argsForZygote.add("--setuid=" + uid);
-            argsForZygote.add("--setgid=" + gid);
-            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
-                argsForZygote.add("--enable-jni-logging");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
-                argsForZygote.add("--enable-safemode");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
-                argsForZygote.add("--enable-debugger");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
-                argsForZygote.add("--enable-checkjni");
-            }
-            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
-                argsForZygote.add("--generate-debug-info");
-            }
-            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
-                argsForZygote.add("--always-jit");
-            }
-            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
-                argsForZygote.add("--native-debuggable");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
-                argsForZygote.add("--enable-assert");
-            }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
-                argsForZygote.add("--mount-external-default");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
-                argsForZygote.add("--mount-external-read");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
-                argsForZygote.add("--mount-external-write");
-            }
-            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+        // --runtime-args, --setuid=, --setgid=,
+        // and --setgroups= must go first
+        argsForZygote.add("--runtime-args");
+        argsForZygote.add("--setuid=" + uid);
+        argsForZygote.add("--setgid=" + gid);
+        if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
+            argsForZygote.add("--enable-jni-logging");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+            argsForZygote.add("--enable-safemode");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+            argsForZygote.add("--enable-debugger");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+            argsForZygote.add("--enable-checkjni");
+        }
+        if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
+            argsForZygote.add("--generate-debug-info");
+        }
+        if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+            argsForZygote.add("--always-jit");
+        }
+        if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+            argsForZygote.add("--native-debuggable");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+            argsForZygote.add("--enable-assert");
+        }
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+            argsForZygote.add("--mount-external-default");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+            argsForZygote.add("--mount-external-read");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+            argsForZygote.add("--mount-external-write");
+        }
+        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
-            //TODO optionally enable debuger
-            //argsForZygote.add("--enable-debugger");
+        //TODO optionally enable debuger
+        //argsForZygote.add("--enable-debugger");
 
-            // --setgroups is a comma-separated list
-            if (gids != null && gids.length > 0) {
-                StringBuilder sb = new StringBuilder();
-                sb.append("--setgroups=");
+        // --setgroups is a comma-separated list
+        if (gids != null && gids.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("--setgroups=");
 
-                int sz = gids.length;
-                for (int i = 0; i < sz; i++) {
-                    if (i != 0) {
-                        sb.append(',');
-                    }
-                    sb.append(gids[i]);
+            int sz = gids.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
                 }
-
-                argsForZygote.add(sb.toString());
+                sb.append(gids[i]);
             }
 
-            if (niceName != null) {
-                argsForZygote.add("--nice-name=" + niceName);
+            argsForZygote.add(sb.toString());
+        }
+
+        if (niceName != null) {
+            argsForZygote.add("--nice-name=" + niceName);
+        }
+
+        if (seInfo != null) {
+            argsForZygote.add("--seinfo=" + seInfo);
+        }
+
+        if (instructionSet != null) {
+            argsForZygote.add("--instruction-set=" + instructionSet);
+        }
+
+        if (appDataDir != null) {
+            argsForZygote.add("--app-data-dir=" + appDataDir);
+        }
+
+        argsForZygote.add(processClass);
+
+        if (extraArgs != null) {
+            for (String arg : extraArgs) {
+                argsForZygote.add(arg);
             }
+        }
 
-            if (seInfo != null) {
-                argsForZygote.add("--seinfo=" + seInfo);
-            }
-
-            if (instructionSet != null) {
-                argsForZygote.add("--instruction-set=" + instructionSet);
-            }
-
-            if (appDataDir != null) {
-                argsForZygote.add("--app-data-dir=" + appDataDir);
-            }
-
-            argsForZygote.add(processClass);
-
-            if (extraArgs != null) {
-                for (String arg : extraArgs) {
-                    argsForZygote.add(arg);
-                }
-            }
-
+        synchronized(mLock) {
             return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
         }
     }
@@ -406,7 +418,9 @@
      */
     public void establishZygoteConnectionForAbi(String abi) {
         try {
-            openZygoteSocketIfNeeded(abi);
+            synchronized(mLock) {
+                openZygoteSocketIfNeeded(abi);
+            }
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
         }
@@ -414,9 +428,12 @@
 
     /**
      * Tries to open socket to Zygote process if not already open. If
-     * already open, does nothing.  May block and retry.
+     * already open, does nothing.  May block and retry.  Requires that mLock be held.
      */
+    @GuardedBy("mLock")
     private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
+
         if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
             try {
                 primaryZygoteState = ZygoteState.connect(mSocket);
@@ -444,4 +461,28 @@
 
         throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
     }
+
+    /**
+     * Instructs the zygote to pre-load the classes and native libraries at the given paths
+     * for the specified abi. Not all zygotes support this function.
+     */
+    public void preloadPackageForAbi(String packagePath, String libsPath, String abi)
+            throws ZygoteStartFailedEx, IOException {
+        synchronized(mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.writer.write("3");
+            state.writer.newLine();
+
+            state.writer.write("--preload-package");
+            state.writer.newLine();
+
+            state.writer.write(packagePath);
+            state.writer.newLine();
+
+            state.writer.write(libsPath);
+            state.writer.newLine();
+
+            state.writer.flush();
+        }
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 25cc961..5e9380e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7932,10 +7932,16 @@
                 BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
         /** {@hide} */
         public static final String
+                BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_";
+        /** {@hide} */
+        public static final String
                 BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
         /** {@hide} */
         public static final String
                 BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
+        /** {@hide} */
+        public static final String
+                BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
 
         /**
          * Device Idle (Doze) specific settings.
@@ -8113,6 +8119,14 @@
         }
 
         /**
+         * Get the key that retrieves a bluetooth pan client priority.
+         * @hide
+         */
+        public static final String getBluetoothPanPriorityKey(String address) {
+            return BLUETOOTH_PAN_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
+        /**
          * Get the key that retrieves a bluetooth map priority.
          * @hide
          */
@@ -8121,6 +8135,14 @@
         }
 
         /**
+         * Get the key that retrieves a bluetooth map client priority.
+         * @hide
+         */
+        public static final String getBluetoothMapClientPriorityKey(String address) {
+            return BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
+        /**
          * Get the key that retrieves a bluetooth pbap client priority.
          * @hide
          */
@@ -8129,7 +8151,7 @@
         }
 
         /**
-         * Get the key that retrieves a bluetooth map priority.
+         * Get the key that retrieves a bluetooth sap priority.
          * @hide
          */
         public static final String getBluetoothSapPriorityKey(String address) {
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index 60e4adf..d9556aa 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -17,19 +17,24 @@
 
 package android.util.jar;
 
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
 import dalvik.system.CloseGuard;
+import java.io.FileDescriptor;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.RandomAccessFile;
 import java.security.cert.Certificate;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.jar.JarFile;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 import java.util.zip.ZipEntry;
-import java.util.jar.JarFile;
+import libcore.io.IoBridge;
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
@@ -46,7 +51,7 @@
 
     // NOTE: It's possible to share a file descriptor with the native
     // code, at the cost of some additional complexity.
-    private final RandomAccessFile raf;
+    private final FileDescriptor fd;
 
     private final StrictJarManifest manifest;
     private final StrictJarVerifier verifier;
@@ -61,8 +66,30 @@
         this(fileName, true, true);
     }
 
+    public StrictJarFile(FileDescriptor fd)
+            throws IOException, SecurityException {
+        this(fd, true, true);
+    }
+
+    public StrictJarFile(FileDescriptor fd,
+            boolean verify,
+            boolean signatureSchemeRollbackProtectionsEnforced)
+                    throws IOException, SecurityException {
+        this("[fd:" + fd.getInt$() + "]", fd, verify,
+                signatureSchemeRollbackProtectionsEnforced);
+    }
+
+    public StrictJarFile(String fileName,
+            boolean verify,
+            boolean signatureSchemeRollbackProtectionsEnforced)
+                    throws IOException, SecurityException {
+        this(fileName, IoBridge.open(fileName, OsConstants.O_RDONLY),
+                verify, signatureSchemeRollbackProtectionsEnforced);
+    }
+
     /**
-     *
+     * @param name of the archive (not necessarily a path).
+     * @param fd seekable file descriptor for the JAR file.
      * @param verify whether to verify the file's JAR signatures and collect the corresponding
      *        signer certificates.
      * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
@@ -70,12 +97,13 @@
      *        {@code false} to ignore any such protections. This parameter is ignored when
      *        {@code verify} is {@code false}.
      */
-    public StrictJarFile(String fileName,
+    private StrictJarFile(String name,
+            FileDescriptor fd,
             boolean verify,
             boolean signatureSchemeRollbackProtectionsEnforced)
                     throws IOException, SecurityException {
-        this.nativeHandle = nativeOpenJarFile(fileName);
-        this.raf = new RandomAccessFile(fileName, "r");
+        this.nativeHandle = nativeOpenJarFile(name, fd.getInt$());
+        this.fd = fd;
 
         try {
             // Read the MANIFEST and signature files up front and try to
@@ -86,14 +114,14 @@
                 this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
                 this.verifier =
                         new StrictJarVerifier(
-                                fileName,
+                                name,
                                 manifest,
                                 metaEntries,
                                 signatureSchemeRollbackProtectionsEnforced);
                 Set<String> files = manifest.getEntries().keySet();
                 for (String file : files) {
                     if (findEntry(file) == null) {
-                        throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+                        throw new SecurityException("File " + file + " in manifest does not exist");
                     }
                 }
 
@@ -105,7 +133,7 @@
             }
         } catch (IOException | SecurityException e) {
             nativeClose(this.nativeHandle);
-            IoUtils.closeQuietly(this.raf);
+            IoUtils.closeQuietly(fd);
             throw e;
         }
 
@@ -192,10 +220,12 @@
 
     public void close() throws IOException {
         if (!closed) {
-            guard.close();
+            if (guard != null) {
+                guard.close();
+            }
 
             nativeClose(nativeHandle);
-            IoUtils.closeQuietly(raf);
+            IoUtils.closeQuietly(fd);
             closed = true;
         }
     }
@@ -214,11 +244,11 @@
 
     private InputStream getZipInputStream(ZipEntry ze) {
         if (ze.getMethod() == ZipEntry.STORED) {
-            return new RAFStream(raf, ze.getDataOffset(),
+            return new FDStream(fd, ze.getDataOffset(),
                     ze.getDataOffset() + ze.getSize());
         } else {
-            final RAFStream wrapped = new RAFStream(
-                    raf, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
+            final FDStream wrapped = new FDStream(
+                    fd, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
 
             int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L));
             return new ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze);
@@ -396,7 +426,7 @@
     }
 
     /**
-     * Wrap a stream around a RandomAccessFile.  The RandomAccessFile is shared
+     * Wrap a stream around a FileDescriptor.  The file descriptor is shared
      * among all streams returned by getInputStream(), so we have to synchronize
      * access to it.  (We can optimize this by adding buffering here to reduce
      * collisions.)
@@ -405,22 +435,17 @@
      *
      * @hide
      */
-    public static class RAFStream extends InputStream {
-        private final RandomAccessFile sharedRaf;
+    public static class FDStream extends InputStream {
+        private final FileDescriptor fd;
         private long endOffset;
         private long offset;
 
-
-        public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) {
-            sharedRaf = raf;
+        public FDStream(FileDescriptor fd, long initialOffset, long endOffset) {
+            this.fd = fd;
             offset = initialOffset;
             this.endOffset = endOffset;
         }
 
-        public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
-            this(raf, initialOffset, raf.length());
-        }
-
         @Override public int available() throws IOException {
             return (offset < endOffset ? 1 : 0);
         }
@@ -430,13 +455,17 @@
         }
 
         @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-            synchronized (sharedRaf) {
+            synchronized (this.fd) {
                 final long length = endOffset - offset;
                 if (byteCount > length) {
                     byteCount = (int) length;
                 }
-                sharedRaf.seek(offset);
-                int count = sharedRaf.read(buffer, byteOffset, byteCount);
+                try {
+                    Os.lseek(fd, offset, OsConstants.SEEK_SET);
+                } catch (ErrnoException e) {
+                    throw new IOException(e);
+                }
+                int count = IoBridge.read(fd, buffer, byteOffset, byteCount);
                 if (count > 0) {
                     offset += count;
                     return count;
@@ -455,8 +484,8 @@
         }
     }
 
-
-    private static native long nativeOpenJarFile(String fileName) throws IOException;
+    private static native long nativeOpenJarFile(String name, int fd)
+            throws IOException;
     private static native long nativeStartIteration(long nativeHandle, String prefix);
     private static native ZipEntry nativeNextEntry(long iterationHandle);
     private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName);
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 15eb8de..f41a838 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -472,6 +472,9 @@
             // Log and discard errors at this stage as we must not crash the system server.
             Log.e(LOGTAG, "error preparing webview native library", t);
         }
+
+        WebViewZygote.onWebViewProviderChanged(packageInfo);
+
         return prepareWebViewInSystemServer(nativeLibs);
     }
 
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
new file mode 100644
index 0000000..bc6e7b4
--- /dev/null
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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 android.webkit;
+
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.SystemService;
+import android.os.ZygoteProcess;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class WebViewZygote {
+    private static final String LOGTAG = "WebViewZygote";
+
+    private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
+    private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
+
+    private static ZygoteProcess sZygote;
+
+    private static PackageInfo sPackage;
+
+    private static boolean sMultiprocessEnabled = false;
+
+    public static ZygoteProcess getProcess() {
+        connectToZygoteIfNeeded();
+        return sZygote;
+    }
+
+    public static String getPackageName() {
+        return sPackage.packageName;
+    }
+
+    public static void setMultiprocessEnabled(boolean enabled) {
+        sMultiprocessEnabled = enabled;
+
+        // When toggling between multi-process being on/off, start or stop the
+        // service. If it is enabled and the zygote is not yet started, bring up the service.
+        // Otherwise, bring down the service. The name may be null if the package
+        // information has not yet been resolved.
+        final String serviceName = getServiceName();
+        if (serviceName == null) return;
+
+        if (enabled && sZygote == null) {
+            SystemService.start(serviceName);
+        } else {
+            SystemService.stop(serviceName);
+            sZygote = null;
+        }
+    }
+
+    public static void onWebViewProviderChanged(PackageInfo packageInfo) {
+        sPackage = packageInfo;
+
+        // If multi-process is not enabled, then do not start the zygote service.
+        if (!sMultiprocessEnabled) {
+            return;
+        }
+
+        final String serviceName = getServiceName();
+
+        if (SystemService.isStopped(serviceName)) {
+            SystemService.start(serviceName);
+        } else if (sZygote != null) {
+            SystemService.restart(serviceName);
+        }
+
+        try {
+            SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
+        } catch (TimeoutException e) {
+            Log.e(LOGTAG, "Timed out waiting for " + serviceName);
+            return;
+        }
+
+        connectToZygoteIfNeeded();
+    }
+
+    private static String getServiceName() {
+        if (sPackage == null)
+            return null;
+
+        if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
+                    sPackage.applicationInfo.primaryCpuAbi)) {
+            return WEBVIEW_ZYGOTE_SERVICE_64;
+        }
+
+        return WEBVIEW_ZYGOTE_SERVICE_32;
+    }
+
+    private static void connectToZygoteIfNeeded() {
+        if (sZygote != null)
+            return;
+
+        if (sPackage == null) {
+            Log.e(LOGTAG, "Cannot connect to zygote, no package specified");
+            return;
+        }
+
+        final String serviceName = getServiceName();
+        if (!SystemService.isRunning(serviceName)) {
+            Log.e(LOGTAG, serviceName + " is not running");
+            return;
+        }
+
+        try {
+            sZygote = new ZygoteProcess("webview_zygote", null);
+
+            String packagePath = sPackage.applicationInfo.sourceDir;
+            String libsPath = sPackage.applicationInfo.nativeLibraryDir;
+
+            Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath);
+            sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Error connecting to " + serviceName, e);
+            sZygote = null;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 2ed7aa2..11dd0e8 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.os;
 
+import android.net.LocalSocket;
+import android.os.Build;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+
 /**
  * Startup class for the WebView zygote process.
  *
@@ -26,7 +35,48 @@
 class WebViewZygoteInit {
     public static final String TAG = "WebViewZygoteInit";
 
+    private static ZygoteServer sServer;
+
+    private static class WebViewZygoteServer extends ZygoteServer {
+        @Override
+        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+                throws IOException {
+            return new WebViewZygoteConnection(socket, abiList);
+        }
+    }
+
+    private static class WebViewZygoteConnection extends ZygoteConnection {
+        WebViewZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+            super(socket, abiList);
+        }
+
+        @Override
+        protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+            // TODO: Use preload information to setup the ClassLoader.
+            return false;
+        }
+    }
+
     public static void main(String argv[]) {
-        throw new RuntimeException("Not implemented yet");
+        sServer = new WebViewZygoteServer();
+
+        // Zygote goes into its own process group.
+        try {
+            Os.setpgid(0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to setpgid(0,0)", ex);
+        }
+
+        try {
+            sServer.registerServerSocket("webview_zygote");
+            sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
+            sServer.closeServerSocket();
+        } catch (Zygote.MethodAndArgsCaller caller) {
+            caller.run();
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fatal exception:", e);
+        }
+
+        System.exit(0);
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 132b022..66b294d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -43,6 +43,7 @@
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import libcore.io.IoUtils;
 
 /**
@@ -169,6 +170,11 @@
                 return handleAbiListQuery();
             }
 
+            if (parsedArgs.preloadPackage != null) {
+                return handlePreloadPackage(parsedArgs.preloadPackage,
+                        parsedArgs.preloadPackageLibs);
+            }
+
             if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
                 throw new ZygoteSecurityException("Client may not specify capabilities: " +
                         "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -270,6 +276,10 @@
         }
     }
 
+    protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+        throw new RuntimeException("Zyogte does not support package preloading");
+    }
+
     /**
      * Closes socket associated with this connection.
      */
@@ -375,6 +385,12 @@
         String appDataDir;
 
         /**
+         * Whether to preload a package, with the package path in the remainingArgs.
+         */
+        String preloadPackage;
+        String preloadPackageLibs;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -532,6 +548,9 @@
                     instructionSet = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.startsWith("--app-data-dir=")) {
                     appDataDir = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--preload-package")) {
+                    preloadPackage = args[++curArg];
+                    preloadPackageLibs = args[++curArg];
                 } else {
                     break;
                 }
@@ -541,6 +560,11 @@
                 if (args.length - curArg > 0) {
                     throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
                 }
+            } else if (preloadPackage != null) {
+                if (args.length - curArg > 0) {
+                    throw new IllegalArgumentException(
+                            "Unexpected arguments after --preload-package.");
+                }
             } else {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 1c307c9..9979b84 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -258,7 +258,7 @@
                     continue;
                 }
 
-                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClass " + line);
+                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                 try {
                     if (false) {
                         Log.v(TAG, "Preloading " + line + "...");
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index ab876410..126d9e7 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -19,6 +19,7 @@
 import static android.system.OsConstants.POLLIN;
 
 import android.net.LocalServerSocket;
+import android.net.LocalSocket;
 import android.system.Os;
 import android.system.ErrnoException;
 import android.system.StructPollfd;
@@ -80,13 +81,18 @@
      */
     private ZygoteConnection acceptCommandPeer(String abiList) {
         try {
-            return new ZygoteConnection(mServerSocket.accept(), abiList);
+            return createNewConnection(mServerSocket.accept(), abiList);
         } catch (IOException ex) {
             throw new RuntimeException(
                     "IOException during accept()", ex);
         }
     }
 
+    protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+            throws IOException {
+        return new ZygoteConnection(socket, abiList);
+    }
+
     /**
      * Close and clean up zygote sockets. Called on shutdown and on the
      * child's exit path.
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bb55610..143cc2e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -250,6 +250,7 @@
     libicuuc \
     libicui18n \
     libmedia \
+    libaudioclient \
     libjpeg \
     libusbhost \
     libharfbuzz_ng \
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index d2d39cd..9deb125 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -13,6 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "PdfEditor"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android/log.h>
+#include <utils/Log.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -29,11 +38,6 @@
 #include "SkMatrix.h"
 
 #include <core_jni_helpers.h>
-#include <vector>
-#include <utils/Log.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 namespace android {
 
@@ -142,8 +146,7 @@
             if (errno == EINTR) {
                 continue;
             }
-            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
-                    "Error writing to buffer: %d", errno);
+            ALOGE("Error writing to buffer: %d", errno);
             return false;
         }
         remainingBytes -= writtenByteCount;
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 6431b94..fd9e714 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -45,6 +45,7 @@
 
 #include "core_jni_helpers.h"
 
+#include "ScopedUtfChars.h"
 
 #define LOG_TRACE(...)
 //#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -264,103 +265,108 @@
         ALOGD("loadNativeCode_native");
     }
 
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    ScopedUtfChars pathStr(env, path);
     std::unique_ptr<NativeCode> code;
-    bool needNativeBridge = false;
+    bool needs_native_bridge = false;
+    std::string error_msg;
 
-    void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, libraryPath);
-    if (handle == NULL) {
-        if (NativeBridgeIsSupported(pathStr)) {
-            handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
-            needNativeBridge = true;
-        }
+    void* handle = OpenNativeLibrary(env,
+                                     sdkVersion,
+                                     pathStr.c_str(),
+                                     classLoader,
+                                     libraryPath,
+                                     &needs_native_bridge,
+                                     &error_msg);
+
+    if (handle == nullptr) {
+        ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
+              pathStr.c_str(),
+              error_msg.c_str());
+        return 0;
     }
-    env->ReleaseStringUTFChars(path, pathStr);
 
-    if (handle != NULL) {
-        void* funcPtr = NULL;
-        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
-        if (needNativeBridge) {
-            funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
-        } else {
-            funcPtr = dlsym(handle, funcStr);
-        }
-
-        code.reset(new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr));
-        env->ReleaseStringUTFChars(funcName, funcStr);
-
-        if (code->createActivityFunc == NULL) {
-            ALOGW("ANativeActivity_onCreate not found");
-            return 0;
-        }
-        
-        code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
-        if (code->messageQueue == NULL) {
-            ALOGW("Unable to retrieve native MessageQueue");
-            return 0;
-        }
-        
-        int msgpipe[2];
-        if (pipe(msgpipe)) {
-            ALOGW("could not create pipe: %s", strerror(errno));
-            return 0;
-        }
-        code->mainWorkRead = msgpipe[0];
-        code->mainWorkWrite = msgpipe[1];
-        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
-        SLOGW_IF(result != 0, "Could not make main work read pipe "
-                "non-blocking: %s", strerror(errno));
-        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
-        SLOGW_IF(result != 0, "Could not make main work write pipe "
-                "non-blocking: %s", strerror(errno));
-        code->messageQueue->getLooper()->addFd(
-                code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code.get());
-        
-        code->ANativeActivity::callbacks = &code->callbacks;
-        if (env->GetJavaVM(&code->vm) < 0) {
-            ALOGW("NativeActivity GetJavaVM failed");
-            return 0;
-        }
-        code->env = env;
-        code->clazz = env->NewGlobalRef(clazz);
-
-        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
-        code->internalDataPathObj = dirStr;
-        code->internalDataPath = code->internalDataPathObj.string();
-        env->ReleaseStringUTFChars(internalDataDir, dirStr);
-    
-        if (externalDataDir != NULL) {
-            dirStr = env->GetStringUTFChars(externalDataDir, NULL);
-            code->externalDataPathObj = dirStr;
-            env->ReleaseStringUTFChars(externalDataDir, dirStr);
-        }
-        code->externalDataPath = code->externalDataPathObj.string();
-
-        code->sdkVersion = sdkVersion;
-        
-        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
-
-        if (obbDir != NULL) {
-            dirStr = env->GetStringUTFChars(obbDir, NULL);
-            code->obbPathObj = dirStr;
-            env->ReleaseStringUTFChars(obbDir, dirStr);
-        }
-        code->obbPath = code->obbPathObj.string();
-
-        jbyte* rawSavedState = NULL;
-        jsize rawSavedSize = 0;
-        if (savedState != NULL) {
-            rawSavedState = env->GetByteArrayElements(savedState, NULL);
-            rawSavedSize = env->GetArrayLength(savedState);
-        }
-
-        code->createActivityFunc(code.get(), rawSavedState, rawSavedSize);
-
-        if (rawSavedState != NULL) {
-            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
-        }
+    void* funcPtr = NULL;
+    const char* funcStr = env->GetStringUTFChars(funcName, NULL);
+    if (needs_native_bridge) {
+        funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
+    } else {
+        funcPtr = dlsym(handle, funcStr);
     }
-    
+
+    code.reset(new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr));
+    env->ReleaseStringUTFChars(funcName, funcStr);
+
+    if (code->createActivityFunc == NULL) {
+        ALOGW("ANativeActivity_onCreate not found");
+        return 0;
+    }
+
+    code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
+    if (code->messageQueue == NULL) {
+        ALOGW("Unable to retrieve native MessageQueue");
+        return 0;
+    }
+
+    int msgpipe[2];
+    if (pipe(msgpipe)) {
+        ALOGW("could not create pipe: %s", strerror(errno));
+        return 0;
+    }
+    code->mainWorkRead = msgpipe[0];
+    code->mainWorkWrite = msgpipe[1];
+    int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
+    SLOGW_IF(result != 0, "Could not make main work read pipe "
+            "non-blocking: %s", strerror(errno));
+    result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
+    SLOGW_IF(result != 0, "Could not make main work write pipe "
+            "non-blocking: %s", strerror(errno));
+    code->messageQueue->getLooper()->addFd(
+            code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code.get());
+
+    code->ANativeActivity::callbacks = &code->callbacks;
+    if (env->GetJavaVM(&code->vm) < 0) {
+        ALOGW("NativeActivity GetJavaVM failed");
+        return 0;
+    }
+    code->env = env;
+    code->clazz = env->NewGlobalRef(clazz);
+
+    const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
+    code->internalDataPathObj = dirStr;
+    code->internalDataPath = code->internalDataPathObj.string();
+    env->ReleaseStringUTFChars(internalDataDir, dirStr);
+
+    if (externalDataDir != NULL) {
+        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
+        code->externalDataPathObj = dirStr;
+        env->ReleaseStringUTFChars(externalDataDir, dirStr);
+    }
+    code->externalDataPath = code->externalDataPathObj.string();
+
+    code->sdkVersion = sdkVersion;
+
+    code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
+
+    if (obbDir != NULL) {
+        dirStr = env->GetStringUTFChars(obbDir, NULL);
+        code->obbPathObj = dirStr;
+        env->ReleaseStringUTFChars(obbDir, dirStr);
+    }
+    code->obbPath = code->obbPathObj.string();
+
+    jbyte* rawSavedState = NULL;
+    jsize rawSavedSize = 0;
+    if (savedState != NULL) {
+        rawSavedState = env->GetByteArrayElements(savedState, NULL);
+        rawSavedSize = env->GetArrayLength(savedState);
+    }
+
+    code->createActivityFunc(code.get(), rawSavedState, rawSavedSize);
+
+    if (rawSavedState != NULL) {
+        env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+    }
+
     return (jlong)code.release();
 }
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 2a46edf..dd5f755 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -297,7 +297,8 @@
                 whichHeap = HEAP_TTF;
                 is_swappable = true;
             } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
-                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
+                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0) ||
+                       (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0)) {
                 whichHeap = HEAP_DEX;
                 is_swappable = true;
             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 13e3f0d..10090a1 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -24,8 +24,9 @@
 #include "android_os_HwRemoteBinder.h"
 
 #include <JNIHelp.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <hidl/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
 #include <hidl/Status.h>
 #include <hwbinder/ProcessState.h>
 #include <nativehelper/ScopedLocalRef.h>
@@ -33,6 +34,8 @@
 #include "core_jni_helpers.h"
 
 using android::AndroidRuntime;
+using android::hardware::hidl_vec;
+using android::hardware::hidl_string;
 
 #define PACKAGE_PATH    "android/os"
 #define CLASS_NAME      "HwBinder"
@@ -40,10 +43,15 @@
 
 namespace android {
 
+static jclass gArrayListClass;
+static struct {
+    jmethodID size;
+    jmethodID get;
+} gArrayListMethods;
+
 static struct fields_t {
     jfieldID contextID;
     jmethodID onTransactID;
-
 } gFields;
 
 // static
@@ -196,69 +204,98 @@
 }
 
 static void JHwBinder_native_registerService(
-        JNIEnv *env, jobject thiz, jstring serviceNameObj) {
+        JNIEnv *env,
+        jobject thiz,
+        jobject interfaceChainArrayList,
+        jstring serviceNameObj) {
     if (serviceNameObj == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", NULL);
         return;
     }
 
-    const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
-
+    const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
     if (serviceName == NULL) {
         return;  // XXX exception already pending?
     }
 
-    const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0);
+    jint numInterfaces = env->CallIntMethod(interfaceChainArrayList,
+                                            gArrayListMethods.size);
+    hidl_string *strings = new hidl_string[numInterfaces];
+
+    for (jint i = 0; i < numInterfaces; i++) {
+        jstring strObj = static_cast<jstring>(
+            env->CallObjectMethod(interfaceChainArrayList,
+                                  gArrayListMethods.get,
+                                  i)
+        );
+        const char * str = env->GetStringUTFChars(strObj, nullptr);
+        strings[i] = hidl_string(str);
+        env->ReleaseStringUTFChars(strObj, str);
+    }
+
+    hidl_vec<hidl_string> interfaceChain;
+    interfaceChain.setToExternal(strings, numInterfaces, true /* shouldOwn */);
+
+    using android::hidl::manager::V1_0::IServiceManager;
 
     sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
 
-    status_t err = hardware::defaultServiceManager()->addService(
-                String16(
-                    reinterpret_cast<const char16_t *>(serviceName),
-                    env->GetStringLength(serviceNameObj)),
-                binder,
-                kVersion);
+    bool ok = hardware::defaultServiceManager()->add(
+                interfaceChain,
+                serviceName,
+                binder);
 
-    env->ReleaseStringCritical(serviceNameObj, serviceName);
+    env->ReleaseStringUTFChars(serviceNameObj, serviceName);
     serviceName = NULL;
 
-    if (err == OK) {
+    if (ok) {
         LOG(INFO) << "Starting thread pool.";
         ::android::hardware::ProcessState::self()->startThreadPool();
     }
 
-    signalExceptionForError(env, err);
+    signalExceptionForError(env, (ok ? OK : UNKNOWN_ERROR));
 }
 
 static jobject JHwBinder_native_getService(
-        JNIEnv *env, jclass /* clazzObj */, jstring serviceNameObj) {
+        JNIEnv *env,
+        jclass /* clazzObj */,
+        jstring ifaceNameObj,
+        jstring serviceNameObj) {
+
+    if (ifaceNameObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return NULL;
+    }
     if (serviceNameObj == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", NULL);
         return NULL;
     }
 
-    const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
-
+    const char *ifaceName = env->GetStringUTFChars(ifaceNameObj, NULL);
+    if (ifaceName == NULL) {
+        return NULL; // XXX exception already pending?
+    }
+    const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
     if (serviceName == NULL) {
-        return NULL;  // XXX exception already pending?
+        env->ReleaseStringUTFChars(ifaceNameObj, ifaceName);
+        return NULL; // XXX exception already pending?
     }
 
-    const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0);
-
     LOG(INFO) << "looking for service '"
-              << String8(String16(
-                          reinterpret_cast<const char16_t *>(serviceName),
-                          env->GetStringLength(serviceNameObj))).string()
+              << serviceName
               << "'";
 
-    sp<hardware::IBinder> service =
-        hardware::defaultServiceManager()->getService(
-                String16(
-                    reinterpret_cast<const char16_t *>(serviceName),
-                    env->GetStringLength(serviceNameObj)),
-                kVersion);
+    sp<hardware::IBinder> service;
+    hardware::defaultServiceManager()->get(
+            ifaceName,
+            serviceName,
+            [&service](sp<hardware::IBinder> out) {
+                service = out;
+            });
 
-    env->ReleaseStringCritical(serviceNameObj, serviceName);
+    env->ReleaseStringUTFChars(ifaceNameObj, ifaceName);
+    ifaceName = NULL;
+    env->ReleaseStringUTFChars(serviceNameObj, serviceName);
     serviceName = NULL;
 
     if (service == NULL) {
@@ -280,16 +317,21 @@
         "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
         (void *)JHwBinder_native_transact },
 
-    { "registerService", "(Ljava/lang/String;)V",
+    { "registerService", "(Ljava/util/ArrayList;Ljava/lang/String;)V",
         (void *)JHwBinder_native_registerService },
 
-    { "getService", "(Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
+    { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
         (void *)JHwBinder_native_getService },
 };
 
 namespace android {
 
 int register_android_os_HwBinder(JNIEnv *env) {
+    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+    gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
+    gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
+
     return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h
index 6bd82e9..0920488 100644
--- a/core/jni/android_os_HwBlob.h
+++ b/core/jni/android_os_HwBlob.h
@@ -20,6 +20,7 @@
 #include <android-base/macros.h>
 #include <jni.h>
 #include <hidl/HidlSupport.h>
+#include <hwbinder/Parcel.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
 
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 5c879b88..a10d807 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -26,6 +26,7 @@
 
 #include <JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <hidl/HidlTransportSupport.h>
 #include <hidl/Status.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -267,17 +268,17 @@
 
     const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
     if (interfaceName) {
-        hardware::Parcel *parcel =
-            JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
-        status_t err = parcel->writeInterfaceToken(
-                String16(
-                    reinterpret_cast<const char16_t *>(interfaceName),
-                    env->GetStringLength(interfaceNameObj)));
+        String8 nameCopy = String8(String16(
+                reinterpret_cast<const char16_t *>(interfaceName),
+                env->GetStringLength(interfaceNameObj)));
 
         env->ReleaseStringCritical(interfaceNameObj, interfaceName);
         interfaceName = NULL;
 
+        hardware::Parcel *parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+        status_t err = parcel->writeInterfaceToken(nameCopy.string());
         signalExceptionForError(env, err);
     }
 }
@@ -294,17 +295,18 @@
 
     const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
     if (interfaceName) {
-        hardware::Parcel *parcel =
-            JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
-        bool valid = parcel->enforceInterface(
-                String16(
-                    reinterpret_cast<const char16_t *>(interfaceName),
-                    env->GetStringLength(interfaceNameObj)));
+        String8 interfaceNameCopy = String8(String16(
+                reinterpret_cast<const char16_t *>(interfaceName),
+                env->GetStringLength(interfaceNameObj)));
 
         env->ReleaseStringCritical(interfaceNameObj, interfaceName);
         interfaceName = NULL;
 
+        hardware::Parcel *parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+        bool valid = parcel->enforceInterface(interfaceNameCopy.string());
+
         if (!valid) {
             jniThrowException(
                     env,
@@ -382,7 +384,7 @@
     hardware::Parcel *parcel =
         JHwParcel::GetNativeContext(env, thiz)->getParcel();
 
-    status_t err = status.writeToParcel(parcel);
+    status_t err = ::android::hardware::writeToParcel(status, parcel);
     signalExceptionForError(env, err);
 }
 
@@ -393,7 +395,7 @@
         JHwParcel::GetNativeContext(env, thiz)->getParcel();
 
     Status status;
-    status_t err = status.readFromParcel(*parcel);
+    status_t err = ::android::hardware::readFromParcel(&status, *parcel);
     signalExceptionForError(env, err);
 }
 
@@ -424,8 +426,8 @@
     status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle);
 
     if (err == OK) {
-        err = s->writeEmbeddedToParcel(
-                parcel, parentHandle, 0 /* parentOffset */);
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *s, parcel, parentHandle, 0 /* parentOffset */);
     }
 
     signalExceptionForError(env, err);
@@ -452,7 +454,8 @@
     if (err == OK) {                                                           \
         size_t childHandle;                                                    \
                                                                                \
-        err = vec->writeEmbeddedToParcel(                                      \
+        err = ::android::hardware::writeEmbeddedToParcel(                      \
+                *vec,                                                          \
                 parcel,                                                        \
                 parentHandle,                                                  \
                 0 /* parentOffset */,                                          \
@@ -507,7 +510,8 @@
     if (err == OK) {
         size_t childHandle;
 
-        err = vec->writeEmbeddedToParcel(
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *vec,
                 parcel,
                 parentHandle,
                 0 /* parentOffset */,
@@ -567,7 +571,8 @@
         return NULL;
     }
 
-    status_t err = const_cast<hidl_string *>(s)->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+            const_cast<hidl_string *>(s),
             *parcel, parentHandle, 0 /* parentOffset */);
 
     if (err != OK) {
@@ -596,8 +601,8 @@
                                                                                \
     size_t childHandle;                                                        \
                                                                                \
-    status_t err = const_cast<hidl_vec<Type> *>(vec)                           \
-        ->readEmbeddedFromParcel(                                              \
+    status_t err = ::android::hardware::readEmbeddedFromParcel(                \
+                const_cast<hidl_vec<Type> *>(vec),                             \
                 *parcel,                                                       \
                 parentHandle,                                                  \
                 0 /* parentOffset */,                                          \
@@ -638,8 +643,8 @@
 
     size_t childHandle;
 
-    status_t err = const_cast<hidl_vec<bool> *>(vec)
-        ->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+                const_cast<hidl_vec<bool> *>(vec),
                 *parcel,
                 parentHandle,
                 0 /* parentOffset */,
@@ -700,12 +705,13 @@
     }
 
     size_t childHandle;
-    status_t err = const_cast<string_vec *>(vec)->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+            const_cast<string_vec *>(vec),
             *parcel, parentHandle, 0 /* parentOffset */, &childHandle);
 
     for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
-        err = const_cast<hidl_vec<hidl_string> *>(vec)
-            ->readEmbeddedFromParcel(
+        err = android::hardware::readEmbeddedFromParcel(
+                    const_cast<hidl_vec<hidl_string> *>(vec),
                     *parcel,
                     childHandle,
                     i * sizeof(hidl_string),
@@ -759,14 +765,16 @@
 
     if (err == OK) {
         size_t childHandle;
-        err = vec->writeEmbeddedToParcel(
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *vec,
                 parcel,
                 parentHandle,
                 0 /* parentOffset */,
                 &childHandle);
 
         for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
-            err = (*vec)[i].writeEmbeddedToParcel(
+            err = ::android::hardware::writeEmbeddedToParcel(
+                    (*vec)[i],
                     parcel,
                     childHandle,
                     i * sizeof(hidl_string));
@@ -888,15 +896,28 @@
     { "readString", "()Ljava/lang/String;",
         (void *)JHwParcel_native_readString },
 
-    { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector },
-    { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector },
-    { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector },
-    { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector },
-    { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector },
-    { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector },
-    { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector },
+    { "readBoolVectorAsArray", "()[Z",
+        (void *)JHwParcel_native_readBoolVector },
 
-    { "readStringVector", "()[Ljava/lang/String;",
+    { "readInt8VectorAsArray", "()[B",
+        (void *)JHwParcel_native_readInt8Vector },
+
+    { "readInt16VectorAsArray", "()[S",
+        (void *)JHwParcel_native_readInt16Vector },
+
+    { "readInt32VectorAsArray", "()[I",
+        (void *)JHwParcel_native_readInt32Vector },
+
+    { "readInt64VectorAsArray", "()[J",
+        (void *)JHwParcel_native_readInt64Vector },
+
+    { "readFloatVectorAsArray", "()[F",
+        (void *)JHwParcel_native_readFloatVector },
+
+    { "readDoubleVectorAsArray", "()[D",
+        (void *)JHwParcel_native_readDoubleVector },
+
+    { "readStringVectorAsArray", "()[Ljava/lang/String;",
         (void *)JHwParcel_native_readStringVector },
 
     { "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;",
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 3023ba8..1d5d6d5 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -24,7 +24,6 @@
 
 #include <JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <hidl/IServiceManager.h>
 #include <hidl/Status.h>
 #include <nativehelper/ScopedLocalRef.h>
 
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index bfdea8f..2e31c8b 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -51,14 +51,16 @@
                         static_cast<jlong>(entry.offset));
 }
 
-static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
-  ScopedUtfChars fileChars(env, fileName);
-  if (fileChars.c_str() == NULL) {
+static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
+  // Name argument is used for logging, and can be any string.
+  ScopedUtfChars nameChars(env, name);
+  if (nameChars.c_str() == NULL) {
     return static_cast<jlong>(-1);
   }
 
   ZipArchiveHandle handle;
-  int32_t error = OpenArchive(fileChars.c_str(), &handle);
+  int32_t error = OpenArchiveFd(fd, nameChars.c_str(), &handle,
+      false /* owned by Java side */);
   if (error) {
     CloseArchive(handle);
     throwIoException(env, error);
@@ -154,7 +156,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"),
+  NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"),
   NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
   NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
   NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"),
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5202a98..ca09708 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -297,12 +297,6 @@
         bool force_mount_namespace) {
     // See storage config details at http://source.android.com/tech/storage/
 
-    // Create a second private mount namespace for our process
-    if (unshare(CLONE_NEWNS) == -1) {
-        ALOGW("Failed to unshare(): %s", strerror(errno));
-        return false;
-    }
-
     String8 storageSource;
     if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
         storageSource = "/mnt/runtime/default";
@@ -310,10 +304,17 @@
         storageSource = "/mnt/runtime/read";
     } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
         storageSource = "/mnt/runtime/write";
-    } else {
+    } else if (!force_mount_namespace) {
         // Sane default of no storage visible
         return true;
     }
+
+    // Create a second private mount namespace for our process
+    if (unshare(CLONE_NEWNS) == -1) {
+        ALOGW("Failed to unshare(): %s", strerror(errno));
+        return false;
+    }
+
     if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
             NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
         ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 372dc7f..33ba886 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -291,7 +291,7 @@
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
-    <protected-broadcast android:name="android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
     <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -299,7 +299,6 @@
     <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
-    <protected-broadcast android:name="android.net.wifi.nan.STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
     <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
@@ -3035,7 +3034,7 @@
     <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
         @hide -->
     <permission android:name="android.permission.PEERS_MAC_ADDRESS"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|setup" />
 
     <!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
         can use this permission to ensure incoming Nfc messages are from the Nfc stack
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
index 39d2c95f..3a9031e 100644
--- a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
index 3a9031e..39d2c95f 100644
--- a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index b9c364c..217ea4e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index 217ea4e..b9c364c 100644
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
index cd0ca73..e443f45 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
index e443f45..cd0ca73 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
index 39dd3b8..b828430 100644
--- a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
index b828430..39dd3b8 100644
--- a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9c16f7e..bb1aaad 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Foonopsies"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skermslot"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Sit af"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Foutverslag"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Neem foutverslag"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Dit sal inligting oor die huidige toestand van jou toestel insamel om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a3c18ac..7ec2cd2f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"የስልክ አማራጮች"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ማያ ቆልፍ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ኃይል አጥፋ"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"ድንገተኛ አደጋ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"የሳንካ ሪፖርት"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"የሳንካ ሪፖርት ውሰድ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2db0e15..d7eee91 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"خيارات الهاتف"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"تأمين الشاشة"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"إيقاف التشغيل"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"الطوارئ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"تقرير الأخطاء"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"إعداد تقرير بالأخطاء"</string>
     <string name="bugreport_message" msgid="398447048750350456">"سيجمع هذا معلومات حول حالة جهازك الحالي لإرسالها كرسالة إلكترونية، ولكنه سيستغرق وقتًا قليلاً من بدء عرض تقرير بالأخطاء. وحتى يكون جاهزًا للإرسال، الرجاء الانتظار."</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 7f55ba1..6be1323 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon seçimləri"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran kilidi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Söndür"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Təcili"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Baq hesabatı"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Baqı xəbər verin"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bu, sizin hazırkı cihaz durumu haqqında məlumat toplayacaq ki, elektron məktub şəklində göndərsin. Baq raportuna başlamaq üçün bir az vaxt lazım ola bilər, bir az səbr edin."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 7e27050..4b9c47a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -209,6 +209,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcije telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaključaj ekran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hitni poziv"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izveštaj o grešci"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Napravi izveštaj o grešci"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupiti informacije o trenutnom stanju uređaja kako bi bile poslate u poruci e-pošte. Od započinjanja izveštaja o grešci do trenutka za njegovo slanje proći će neko vreme; budite strpljivi."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a70d9dc..46b5df0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опции на телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заключване на екрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Изключване"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Спешно обаждане"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Сигнал за програмна грешка"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Сигнал за програмна грешка"</string>
     <string name="bugreport_message" msgid="398447048750350456">"По този начин ще се събере информация за текущото състояние на устройството ви, която да се изпрати като имейл съобщение. След стартирането на процеса ще мине известно време, докато сигналът за програмна грешка бъде готов за подаване. Моля, имайте търпение."</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 2d88a1d..33bce25 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ফোন বিকল্পগুলি"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"স্ক্রীণ লক"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"পাওয়ার বন্ধ করুন"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"জরুরী"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ত্রুটির প্রতিবেদন"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ত্রুটির প্রতিবেদন করুন"</string>
     <string name="bugreport_message" msgid="398447048750350456">"এটি একটি ই-মেল বার্তা পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; দয়া করে ধৈর্য রাখুন৷"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e5a815d..3246054 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcions del telèfon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueig de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apaga"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergències"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe d\'error"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crea informe d\'errors"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Es recopilarà informació sobre l\'estat actual del dispositiu i se t\'enviarà per correu electrònic. Passaran uns quants minuts des de l\'inici de l\'informe d\'errors fins al seu enviament, per la qual cosa et recomanem que tinguis paciència."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4a142a2..1a0b7bb 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefonu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zámek obrazovky"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vypnout"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Stav nouze"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 762c173..f37764b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Indstillinger for telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skærmlås"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Sluk"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nødopkald"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Fejlrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Lav fejlrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en e-mail. Der går lidt tid, fra fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b163d35..61b1411 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonoptionen"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Displaysperre"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Ausschalten"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Notfall"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status deines Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte habe etwas Geduld."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 100ee81..df3a23a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Επιλογές τηλεφώνου"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Κλείδωμα οθόνης"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Απενεργοποίηση"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Κλήση έκτακτης ανάγκης"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Αναφορά σφαλμάτων"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Λήψη αναφοράς σφάλματος"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Θα συλλέξει πληροφορίες σχετικά με την τρέχουσα κατάσταση της συσκευής σας και θα τις στείλει μέσω μηνύματος ηλεκτρονικού ταχυδρομείου. Απαιτείται λίγος χρόνος για τη σύνταξη της αναφοράς σφάλματος και την αποστολή της. Κάντε λίγη υπομονή."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0522882..cf7f14a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0522882..cf7f14a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0522882..cf7f14a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0786b1a..2af1c7a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opciones de dispositivo"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergencias"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de errores"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Iniciar informe de errores"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo, que se enviará por correo. Pasarán unos minutos desde que se inicie el informe de errores hasta que se envíe, por lo que te recomendamos que tengas paciencia."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1909e54..d052e14 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opciones del teléfono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergencia"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de error"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crear informe de errores"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo y se enviará por correo electrónico. Pasarán unos minutos desde que empiece a generarse el informe de errores hasta que se envíe."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 14695e3..2bc2d0b 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonivalikud"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekraanilukk"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Lülita välja"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hädaabi"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Veaaruanne"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Veaaruande võtmine"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Nii kogutakse teavet teie seadme praeguse oleku kohta, et saata see meilisõnumina. Enne kui saate veaaruande ära saata, võtab selle loomine natuke aega; varuge kannatust."</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 98adc86..94f6b48 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonoaren aukerak"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Pantailaren blokeoa"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Itzali"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Larrialdiak"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Akatsen txostena"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Sortu akatsen txostena"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 26684ac..1a40650 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"گزینه‌های تلفن"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"قفل صفحه"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"اضطراری"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
     <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ac827a1..2b4baf9 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Puhelimen asetukset"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Näytön lukitus"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Katkaise virta"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hätäpuhelu"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Virheraportti"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Luo virheraportti"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Toiminto kerää tietoja laitteen tilasta ja lähettää ne sähköpostitse. Virheraportti on valmis lähetettäväksi hetken kuluttua - kiitos kärsivällisyydestäsi."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 57119da..b4d34eb 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Options du téléphone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Verrouillage de l\'écran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgence"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bogue"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bogue"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme de courriel. Merci de patienter pendant la préparation du rapport de bogue. Cette opération peut prendre quelques instants."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b089991..39d8ebf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Options du téléphone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Verrouillage de l\'écran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgences"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 23669d4..a6024dc 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcións de teléfono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo da pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencia"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 656061d..d8ccfc2 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ફોન વિકલ્પો"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"સ્ક્રીન લૉક"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"પાવર બંધ"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"કટોકટી"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"બગ રિપોર્ટ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"બગ રિપોર્ટ લો"</string>
     <string name="bugreport_message" msgid="398447048750350456">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b52ff25..3bb5e00 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फ़ोन विकल्‍प"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्‍क्रीन लॉक"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"पावर बंद"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"आपातकाल"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट प्राप्त करें"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ईमेल संदेश के रूप में भेजने के लिए, इसके द्वारा आपके डिवाइस की वर्तमान स्थिति के बारे में जानकारी एकत्र की जाएगी. बग रिपोर्ट प्रारंभ करने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया धैर्य रखें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index b90f9dd..07d50dc 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -216,6 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcije telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaključavanje zaslona"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izvješće o bugovima"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Izvješće o programskoj pogrešci"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Time će se prikupiti podaci o trenutačnom stanju vašeg uređaja koje ćete nam poslati u e-poruci. Za pripremu izvješća o programskoj pogrešci potrebno je nešto vremena pa vas molimo za strpljenje."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 8187e43..8586040 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonbeállítások"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Képernyő lezárása"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Kikapcsolás"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Vészhívás"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Programhiba bejelentése"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Hibajelentés készítése"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ezzel információt fog gyűjteni az eszköz jelenlegi állapotáról, amelyet a rendszer e-mailben fog elküldeni. Kérjük, legyen türelemmel, amíg a hibajelentés elkészül, és küldhető állapotba kerül."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 171d1a9..d0904df 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Հեռախոսի ընտրանքներ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Էկրանի փական"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Անջատել"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Արտակարգ իրավիճակ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Գրել սխալի զեկույց"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bd17260..3f98718 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opsi telepon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kunci layar"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan daya"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index b575e79..a0578b0 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Valkostir síma"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skjálás"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Slökkva"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Neyðarsímtal"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Villutilkynning"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Útbúa villutilkynningu"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Þetta safnar upplýsingum um núverandi stöðu tækisins til að senda með tölvupósti. Það tekur smástund frá því villutilkynningin er ræst og þar til hún er tilbúin til sendingar – sýndu biðlund."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index bfadb9d..c0ea60d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opzioni telefono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blocco schermo"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Spegni"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergenza"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Segnalazione di bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Apri segnalazione bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Verranno raccolte informazioni sullo stato corrente del dispositivo che saranno inviate sotto forma di messaggio email. Passerà un po\' di tempo prima che la segnalazione di bug aperta sia pronta per essere inviata; ti preghiamo di avere pazienza."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c2ba6f3..12b2860 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"אפשרויות טלפון"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"נעילת מסך"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"כיבוי"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"חירום"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"דיווח על באג"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"שלח דיווח על באג"</string>
     <string name="bugreport_message" msgid="398447048750350456">"פעולה זו תאסוף מידע על מצב המכשיר הנוכחי שלך על מנת לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת דיווח הבאג ועד לשליחת ההודעה בפועל. אנא המתן בסבלנות."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index c7c404e..c06d8f8 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"携帯電話オプション"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"画面ロック"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"電源を切る"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急通報"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"バグレポート"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"バグレポートを取得"</string>
     <string name="bugreport_message" msgid="398447048750350456">"現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 0d27ea5..11dcebf 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ტელეფონის პარამეტრები"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ეკრანის დაბლოკვა"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"კვების გამორთვა"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"საგანგებო სამსახურები"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ხარვეზის შესახებ ანგარიში"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"შექმენით შეცდომის ანგარიში"</string>
     <string name="bugreport_message" msgid="398447048750350456">"იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ."</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 9aad142..1a40a07 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Телефон опциялары"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Экранды құлыптау"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Өшіру"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Төтенше жағдай"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Вирус туралы хабарлау"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Қате туралы есеп құру"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Құрылғының қазіргі күйі туралы ақпаратты жинап, электрондық хабармен жібереді. Есеп әзір болғанша біраз уақыт кетеді, шыдай тұрыңыз."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 0256ddc..53cef5f 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ជម្រើស​ទូរស័ព្ទ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ចាក់​សោ​អេក្រង់"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"បិទ"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"អាសន្ន"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"របាយការណ៍​កំហុស"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"យក​របាយការណ៍​កំហុស"</string>
     <string name="bugreport_message" msgid="398447048750350456">"វា​នឹង​​ប្រមូល​ព័ត៌មាន​អំពី​ស្ថានភាព​ឧបករណ៍​របស់​អ្នក ដើម្បី​ផ្ញើ​ជា​សារ​អ៊ីមែល។ វា​នឹង​ចំណាយ​ពេល​តិច​ពី​ពេល​ចាប់ផ្ដើម​របាយការណ៍​រហូត​ដល់​ពេល​វា​រួចរាល់​ដើម្បី​ផ្ញើ សូម​អត់ធ្មត់។"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index c172e91..2984afd 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ಫೋನ್ ಆಯ್ಕೆಗಳು"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ಸ್ಕ್ರೀನ್ ಲಾಕ್"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ಪವರ್ ಆಫ್ ಮಾಡು"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"ತುರ್ತು"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ದೋಷದ ವರದಿ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ದೋಷ ವರದಿ ರಚಿಸಿ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ನಿಮ್ಮ ಸಾಧನದ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿಯ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಿಕೊಳ್ಳುವುದರ ಜೊತೆ ಇ-ಮೇಲ್ ರೂಪದಲ್ಲಿ ನಿಮಗೆ ರವಾನಿಸುತ್ತದೆ. ಇದು ದೋಷ ವರದಿಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಸಮಯದಿಂದ ಅದನ್ನು ಕಳುಹಿಸುವವರೆಗೆ ಸ್ವಲ್ಪ ಸಮಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ; ದಯವಿಟ್ಟು ತಾಳ್ಮೆಯಿಂದಿರಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 254adc82..97afc56 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"휴대전화 옵션"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"화면 잠금"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"종료"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"긴급 전화"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"버그 신고"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"버그 신고"</string>
     <string name="bugreport_message" msgid="398447048750350456">"현재 기기 상태에 대한 정보를 수집하여 이메일 메시지로 전송합니다. 버그 신고를 시작하여 전송할 준비가 되려면 약간 시간이 걸립니다."</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 50340d4..6ffd5f6 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Телефон мүмкүнчүлүктөрү"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Экран кулпусу"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Кубатын өчүрүү"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Тез жардам"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ката тууралуу билдирүү"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ката тууралуу билдирүү түзүү"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Бул сиздин түзмөгүңүздүн учурдагы абалын эмейл билдирүүсү катары жөнөтүш максатында маалымат чогултат. Ката тууралуу билдирүү түзүлүп башталып, жөнөтүлгөнгө чейин бир аз убакыт керек болот; сураныч, бир аз күтө туруңуз."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 1ef7622..0f6eabc 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ໂຕເລືອກໂທລະສັບ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ລັອກໜ້າຈໍ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ປິດ"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"ສຸກເສີນ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ລາຍງານຂໍ້ຜິດພາດ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ໃຊ້ລາຍງານຂໍ້ບົກພ່ອງ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ນີ້ຈະເປັນການເກັບກຳຂໍ້ມູນກ່ຽວກັບ ສະຖານະປັດຈຸບັນຂອງອຸປະກອນທ່ານ ເພື່ອສົ່ງເປັນຂໍ້ຄວາມທາງອີເມວ. ມັນຈະໃຊ້ເວລາໜ້ອຍນຶ່ງ ໃນການເລີ່ມຕົ້ນການລາຍງານຂໍ້ຜິດພາດ ຈົນກວ່າຈະພ້ອມທີ່ຈະສົ່ງໄດ້, ກະລຸນາລໍຖ້າ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 80ff67e..80cd452 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefono parinktys"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekrano užraktas"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Išjungiamas maitinimas"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Skambutis pagalbos numeriu"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Pranešimas apie riktą"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Pranešti apie riktą"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie riktą bus paruoštas siųsti; būkite kantrūs."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 210d05b..1fd282e 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -216,6 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Tālruņa opcijas"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekrāna bloķētājs"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Strāvas padeve ir izslēgta."</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Ārkārtas"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Kļūdu ziņojums"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Kļūdu ziņojuma sagatavošana"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Veicot šo darbību, tiks apkopota informācija par jūsu ierīces pašreizējo stāvokli un nosūtīta e-pasta ziņojuma veidā. Kļūdu ziņojuma pabeigšanai un nosūtīšanai var būt nepieciešams laiks. Lūdzu, esiet pacietīgs."</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index a24ad57..a9db0f7 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опции на телефон"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заклучи екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Исклучи"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Итен случај"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Извештај за грешка"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Земи извештај за грешки"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ова ќе собира информации за моменталната состојба на вашиот уред, за да ги испрати како порака по е-пошта. Тоа ќе одземе малку време почнувајќи од извештајот за грешки додека не се подготви за праќање; бидете трпеливи."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index f91db2d..6d77ebb 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ഫോൺ ഓപ്‌ഷനുകൾ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"സ്‌ക്രീൻ ലോക്ക്"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"പവർ ഓഫാക്കുക"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"അടിയന്തിരാവശ്യം"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ബഗ് റിപ്പോർട്ട് എടുക്കുക"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്‌ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്‌ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index f54a485..4f579f8 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Утасны сонголтууд"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Дэлгэцний түгжээ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Унтраах"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Яаралтай тусламж"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Алдаа мэдээллэх"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Согог репорт авах"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Энэ таны төхөөрөмжийн одоогийн статусын талаарх мэдээллийг цуглуулах ба имэйл мессеж болгон илгээнэ. Алдааны мэдэгдлээс эхэлж илгээхэд бэлэн болоход хэсэг хугацаа зарцуулагдана тэвчээртэй байна уу."</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index c7d2191..e4121bf 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फोन पर्याय"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्क्रीन लॉक"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"बंद"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"आणीबाणी"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"दोष अहवाल"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"दोष अहवाल घ्या"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ई-मेल संदेश म्हणून पाठविण्यासाठी, हे आपल्या वर्तमान डिव्हाइस स्थितीविषयी माहिती संकलित करेल. दोष अहवाल प्रारंभ करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index c4123ab..d0ca602 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Pilihan telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kunci skrin"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan kuasa"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Kecemasan"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan pepijat"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan pepijat"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpul maklumat tentang keadaan peranti semasa anda untuk dihantarkan sebagai mesej e-mel. Harap bersabar, mungkin perlu sedikit masa untuk memulakan laporan sehingga siap untuk dihantar."</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 5dbf517..59b7410 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ဖုန်းဆိုင်ရာရွေးချယ်မှုများ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ဖုန်းမျက်နှာပြင်အား သော့ချရန်"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ပါဝါပိတ်ရန်"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"အရေးပေါ်"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းအား ယူရန်"</string>
     <string name="bugreport_message" msgid="398447048750350456">"သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d17d69e..bb73233 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefoninnstillinger"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Lås skjermen"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Slå av"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nødssituasjon"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Utfør feilrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 138c67f..74db0f1 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फोन विकल्पहरू"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्क्रिन बन्द"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"बन्द गर्नुहोस्"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"आपतकालीन"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट लिनुहोस्"</string>
     <string name="bugreport_message" msgid="398447048750350456">"एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c8a804b..f0be632 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefoonopties"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Schermvergrendeling"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Uitschakelen"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Foutenrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Foutenrapport genereren"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Hiermee worden gegevens over de huidige status van je apparaat verzameld en als e-mail verzonden. Wanneer u een foutenrapport start, duurt het even voordat het kan worden verzonden. Even geduld alstublieft."</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index a4e5b89..3e03487 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ਫੋਨ ਚੋਣਾਂ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ਸਕ੍ਰੀਨ ਲੌਕ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ਪਾਵਰ ਬੰਦ"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ਬਗ ਰਿਪੋਰਟ ਲਓ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ਇਹ ਇੱਕ ਈ-ਮੇਲ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, ਤੁਹਾਡੀ ਵਰਤਮਾਨ ਡੀਵਾਈਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਤਰ ਕਰੇਗਾ। ਬਗ ਰਿਪੋਰਟ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਥੋੜ੍ਹਾ ਸਮਾਂ ਲੱਗੇਗਾ ਜਦੋਂ ਤੱਕ ਇਹ ਭੇਜੇ ਜਾਣ ਲਈ ਤਿਆਰ ਨਾ ਹੋਵੇ, ਕਿਰਪਾ ਕਰਕੇ ਧੀਰਜ ਰੱਖੋ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ade4bdc..d2ea263 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcje telefonu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blokada ekranu"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Wyłącz"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowe"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Zgłoś błąd"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c05931e..98fe172 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloquear tela"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 398193e..01ac4e1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueio de ecrã"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c05931e..98fe172 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloquear tela"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index c61b41a..4181014 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -216,6 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opțiuni telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blocați ecranul"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Opriți alimentarea"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgență"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Executați un raport despre erori"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 54fb905..ab54097 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметры телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Блокировка экрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Отключить питание"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Экстренный вызов"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index f5878ce..18fd240 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"දුරකථන විකල්ප"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"තිර අගුල"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"බලය අක්‍රිය කරන්න"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"හදිසි"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"දෝෂ වර්තාව"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"දෝෂ වාර්තාවක් ගන්න"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ඊ-තැපැල් පණිවිඩයක් ලෙස යැවීමට මෙය ඔබගේ වත්මන් උපාංග තත්වය ගැන තොරතුරු එකතු කරනු ඇත. දෝෂ වාර්තාව ආරම්භ කර එය යැවීමට සූදානම් කරන තෙක් එයට කිසියම් කාලයක් ගතවනු ඇත; කරුණාකර ඉවසන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 2cb7ecd..f1a1174 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefónu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zámka obrazovky"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vypnúť"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Tiesňové volanie"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hlásenie o chybách"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Vytvoriť hlásenie chyby"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Týmto zhromaždíte informácie o aktuálnom stave zariadenia. Informácie je potom možné odoslať e-mailom, chvíľu však potrvá, kým bude hlásenie chyby pripravené na odoslanie. Prosíme vás preto o trpezlivosť."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9ece9eb..08ce1ce 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaklep zaslona"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Izklopi"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Klic v sili"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Poročilo o napakah"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ustvari poročilo o napakah"</string>
     <string name="bugreport_message" msgid="398447048750350456">"S tem bodo zbrani podatki o trenutnem stanju naprave, ki bodo poslani v e-poštnem sporočilu. Izvedba poročila o napakah in priprava trajata nekaj časa, zato bodite potrpežljivi."</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 9dcd8e6..4bd5678 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opsionet e telefonit"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kyçja e ekranit"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Fik"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgjenca"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Raporti i defekteve në kod"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Merr raportin e defekteve në kod"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ky funksion mundëson mbledhjen e informacioneve mbi gjendjen aktuale të pajisjes për ta dërguar si mesazh mail-i. Do të duhet pak kohë nga nisja e raportit të defekteve në kod. Faleminderit për durimin."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4eb13f5..3a68c84 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -216,6 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опције телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Закључај екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Искључи"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Хитни позив"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Извештај о грешци"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Направи извештај о грешци"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Овим ће се прикупити информације о тренутном стању уређаја како би биле послате у поруци е-поште. Од започињања извештаја о грешци до тренутка за његово слање проћи ће неко време; будите стрпљиви."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 21016d6..a929756 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonalternativ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skärmlås"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Stäng av"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nödsituation"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Felrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Skapa felrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Nu hämtas information om aktuell status för enheten, som sedan skickas i ett e-postmeddelade. Det tar en liten stund innan felrapporten är färdig och kan skickas, så vi ber dig ha tålamod."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8d9b151..327ffed 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -212,6 +212,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Chaguo za simu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Funga skrini"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Zima"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Dharura"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index c4c5bc3..94145df 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"தொலைபேசி விருப்பங்கள்"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"திரைப் பூட்டு"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"முடக்கு"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"அவசர அழைப்பு"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"பிழை அறிக்கை"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"பிழை அறிக்கையை எடு"</string>
     <string name="bugreport_message" msgid="398447048750350456">"உங்கள் நடப்புச் சாதன நிலையை மின்னஞ்சல் செய்தியாக அனுப்ப, அது குறித்த தகவலை இது சேகரிக்கும். பிழை அறிக்கையைத் தொடங்குவதில் இருந்து, அது அனுப்புவதற்குத் தயாராகும் வரை, இதற்குச் சிறிது நேரம் ஆகும்; பொறுமையாகக் காத்திருக்கவும்."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 1cfab6e..aff7d0e 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ఫోన్ ఎంపికలు"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"స్క్రీన్ లాక్"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"పవర్ ఆఫ్ చేయి"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"అత్యవసరం"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"బగ్ నివేదిక"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"బగ్ నివేదికను సిద్ధం చేయి"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 581bb81..e470e86 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ตัวเลือกโทรศัพท์"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ล็อกหน้าจอ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"เหตุฉุกเฉิน"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string>
     <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index df4dd9b..4bc1ace 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Pagpipilian sa telepono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Pag-lock sa screen"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"I-off"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ulat sa bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Kunin ang ulat sa bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Mangongolekta ito ng impormasyon tungkol sa kasalukuyang katayuan ng iyong device, na ipapadala bilang mensaheng e-mail. Gugugol ito ng kaunting oras mula sa pagsisimula ng ulat sa bug hanggang sa handa na itong maipadala; mangyaring magpasensya."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a1f6f64..4a3d219 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon seçenekleri"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran kilidi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Kapat"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Acil durum"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hata raporu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Hata raporu al"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bu rapor, e-posta iletisi olarak göndermek üzere cihazınızın şu anki durumuyla ilgili bilgi toplar. Hata raporu başlatıldıktan sonra hazır olması biraz zaman alabilir, lütfen sabırlı olun."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c8b3754..b53c1b6 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -218,6 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметри телеф."</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заблок. екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Вимкнути"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 91a8c06..4e5f585 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"فون کے اختیارات"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"اسکرین لاک"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"پاور آف"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"ایمرجنسی"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"بگ کی اطلاع"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"بگ کی اطلاع لیں"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ایک ای میل پیغام کے بطور بھیجنے کیلئے، یہ آپ کے موجودہ آلہ کی حالت کے بارے میں معلومات جمع کرے گا۔ بگ کی اطلاع شروع کرنے سے لے کر بھیجنے کیلئے تیار ہونے تک اس میں تھوڑا وقت لگے گا؛ براہ کرم تحمل سے کام لیں۔"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 32a1328..43514f21 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon sozlamalari"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran qulfi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"O‘chirish"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Favqulodda chaqiruv"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Nosozlik haqida ma’lumot berish"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Xatoliklar hisoboti"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Qurilmangiz holati haqidagi ma’lumotlar to‘planib, e-pochta orqali yuboriladi. Hisobotni tayyorlash biroz vaqt olishi mumkin."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 3b86a37..505d685 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Tùy chọn điện thoại"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Khoá màn hình"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Tắt nguồn"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Khẩn cấp"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Báo cáo lỗi"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Nhận báo cáo lỗi"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Báo cáo này sẽ thu thập thông tin về tình trạng thiết bị hiện tại của bạn, để gửi dưới dạng thông báo qua email. Sẽ mất một chút thời gian kể từ khi bắt đầu báo cáo lỗi cho tới khi báo cáo sẵn sàng để gửi; xin vui lòng kiên nhẫn."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d7490c5..0472726 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"手机选项"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"紧急呼救"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"错误报告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"提交错误报告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"这会收集有关当前设备状态的信息,并以电子邮件的形式进行发送。从开始生成错误报告到准备好发送需要一点时间,请耐心等待。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6c896b2..a313caa 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"手機選項"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"螢幕鎖定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"關閉"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,並以電郵傳送給您。從開始建立錯誤報告到準備傳送需要一段時間,請耐心等候。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f3691cd..c669d4b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"電話選項"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"螢幕鎖定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"關機"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急電話"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,以便透過電子郵件傳送。從錯誤報告開始建立到準備傳送的這段過程可能需要一點時間,敬請耐心等候。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a2f5c13..80d24fd 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -214,6 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Okukhethwa kukho kwefoni"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ukuvala isikrini"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vala amandla"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Isimo esiphuthumayo"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Umbiko wephutha"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Thatha umbiko wesiphazamiso"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Lokhu kuzoqoqa ulwazi mayelana nesimo samanje sedivayisi yakho, ukuthumela imilayezo ye-imeyili. Kuzothatha isikhathi esincane kusuka ekuqaleni umbiko wesiphazamiso uze ulungele ukuthunyelwa; sicela ubekezele."</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 202bf83..0309f74 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1290,6 +1290,9 @@
     <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
     <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
 
+    <!-- Enabling autoconnect over pan -->
+    <bool name="config_bluetooth_pan_enable_autoconnect">false</bool>
+
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
@@ -2162,6 +2165,9 @@
          provisioning, availability etc -->
     <bool name="config_carrier_wfc_ims_available">false</bool>
 
+    <!-- Whether to use voip audio mode for ims call -->
+    <bool name="config_use_voip_mode_for_ims">false</bool>
+
     <bool name="config_networkSamplingWakesDevice">true</bool>
 
     <string-array translatable="false" name="config_cdma_home_system" />
@@ -2194,21 +2200,6 @@
         <item>SUPL_MODE=1</item>
     </string-array>
 
-    <!-- If there is no preload VM number in the sim card, carriers such as
-         Verizon require to load a default vm number from the configurantion.
-         Define config_default_vm_number for this purpose. And there are two
-         optional formats for this configuration as below:
-         (1)<item>voicemail number</item>
-         (2)<item>voicemail number;gid</item>
-         The logic to pick up the correct voicemail number:
-         (1) If the config_default_vm_number array has no gid special item, the last one will be
-         picked
-         (2) If the config_default_vm_number array has gid special item and  it matches the current
-         sim's gid, it will be picked.
-         (3) If the config_default_vm_number array has gid special item but it doesn't match the
-         current sim's gid, the last one without gid will be picked -->
-    <string-array translatable="false" name="config_default_vm_number" />
-
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">0</integer>
 
@@ -2520,4 +2511,11 @@
         <item>405</item>
     </integer-array>
 
+    <!-- Specifies whether the permissions needed by a legacy app should be
+         reviewed before any of its components can run. A legacy app is one
+         with targetSdkVersion < 23, i.e apps using the old permission model.
+         If review is not required, permissions are reviewed before the app
+         is installed. -->
+    <bool name="config_permissionReviewRequired">false</bool>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 54d8912..91f0251 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -364,6 +364,7 @@
   <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
+  <java-symbol type="bool" name="config_bluetooth_pan_enable_autoconnect" />
   <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
@@ -2194,6 +2195,7 @@
   <java-symbol type="bool" name="config_carrier_vt_available" />
   <java-symbol type="bool" name="config_device_wfc_ims_available" />
   <java-symbol type="bool" name="config_carrier_wfc_ims_available" />
+  <java-symbol type="bool" name="config_use_voip_mode_for_ims" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
   <java-symbol type="string" name="whichApplicationNamed" />
@@ -2233,7 +2235,6 @@
   <java-symbol type="bool" name="config_auto_attach_data_on_creation" />
   <java-symbol type="attr" name="closeItemLayout" />
   <java-symbol type="layout" name="resolver_different_item_header" />
-  <java-symbol type="array" name="config_default_vm_number" />
   <java-symbol type="integer" name="config_cdma_3waycall_flash_delay"/>
   <java-symbol type="array" name="config_default_vm_number" />
   <java-symbol type="attr" name="windowBackgroundFallback" />
@@ -2617,4 +2618,7 @@
   <java-symbol type="array" name="config_emergency_mcc_codes" />
 
   <java-symbol type="drawable" name="emergency_icon" />
+
+  <java-symbol type="bool" name="config_permissionReviewRequired" />
+
 </resources>
diff --git a/core/res/res/xml/preferred_time_zones.xml b/core/res/res/xml/preferred_time_zones.xml
deleted file mode 100644
index da8553f..0000000
--- a/core/res/res/xml/preferred_time_zones.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/default/default/data/preferred_time_zones.xml
-**
-** Copyright 2006, 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.
-*/
--->
-<timezones>
-	<timezone offset="-18000000">America/New_York</timezone>
-	<timezone offset="-21600000">America/Chicago</timezone>
-	<timezone offset="-25200000">America/Denver</timezone>
-	<timezone offset="-28800000">America/Los_Angeles</timezone>
-</timezones>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index a9fa5e0..29c6b79 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -57,6 +57,12 @@
     <!-- Bulgaria: 4-5 digits, plus EU -->
     <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}" />
 
+    <!-- Bahrain: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="bh" pattern="\\d{1,5}" free="81181" />
+
+    <!-- Brazil: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d" />
+
     <!-- Belarus: 4 digits -->
     <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
 
@@ -129,7 +135,7 @@
 
     <!-- Italy: 5 digits (premium=4xxxx), plus EU:
          http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
-    <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503" />
+    <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503" standard="43\\d{3}" />
 
     <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
     <shortcode country="jp" free="8083" />
@@ -204,7 +210,7 @@
     <shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
 
     <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
-    <shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
+    <shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
 
     <!-- Thailand: 4186001 used by AIS_TH_DCB -->
     <shortcode country="th" free="4186001" />
@@ -220,9 +226,9 @@
 
     <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
          visual voicemail code for T-Mobile: 122 -->
-    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567" free="122|87902" />
+    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245" />
 
     <!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="vn" pattern="\\d{1,5}" free="5001" />
+    <shortcode country="vn" pattern="\\d{1,5}" free="5001|9055" />
 
 </shortcodes>
diff --git a/core/res/res/xml/time_zones_by_country.xml b/core/res/res/xml/time_zones_by_country.xml
index 9b2bd50..a685e2b 100644
--- a/core/res/res/xml/time_zones_by_country.xml
+++ b/core/res/res/xml/time_zones_by_country.xml
@@ -31,11 +31,11 @@
 
     <!-- ANTIGUA AND BARBUDA, -4:00 -->
 
-    <timezone code="ag">America/Antigua</timezone>
+    <timezone code="ag">America/Port_of_Spain</timezone>
 
     <!-- ANGUILLA, -4:00 -->
 
-    <timezone code="ai">America/Anguilla</timezone>
+    <timezone code="ai">America/Port_of_Spain</timezone>
 
     <!-- ALBANIA, 1:00 -->
 
@@ -45,18 +45,13 @@
 
     <timezone code="am">Asia/Yerevan</timezone>
 
-    <!-- NETHERLANDS ANTILLES, -4:00 -->
-
-    <timezone code="an">America/Curacao</timezone>
-
     <!-- ANGOLA, 1:00 -->
 
-    <timezone code="ao">Africa/Luanda</timezone>
+    <timezone code="ao">Africa/Lagos</timezone>
 
     <!-- ANTARCTICA, 12:00 -->
 
-    <timezone code="aq">Antarctica/McMurdo</timezone>
-    <timezone code="aq">Antarctica/South_Pole</timezone>
+    <timezone code="aq">Pacific/Auckland</timezone>
 
     <!-- ANTARCTICA, 10:00 -->
 
@@ -70,15 +65,22 @@
 
     <timezone code="aq">Antarctica/Davis</timezone>
 
-    <!-- ANTARCTICA, 6:00 -->
+    <!-- ANTARCTICA, 5:00 -->
 
     <timezone code="aq">Antarctica/Mawson</timezone>
+
+    <!-- ANTARCTICA, 6:00 -->
+
     <timezone code="aq">Antarctica/Vostok</timezone>
 
     <!-- ANTARCTICA, 3:00 -->
 
     <timezone code="aq">Antarctica/Syowa</timezone>
 
+    <!-- ANTARCTICA, 0:00 -->
+
+    <timezone code="aq">Antarctica/Troll</timezone>
+
     <!-- ANTARCTICA, -3:00 -->
 
     <timezone code="aq">Antarctica/Rothera</timezone>
@@ -91,12 +93,14 @@
 
     <timezone code="ar">America/Argentina/Buenos_Aires</timezone>
     <timezone code="ar">America/Argentina/Cordoba</timezone>
+    <timezone code="ar">America/Argentina/Salta</timezone>
     <timezone code="ar">America/Argentina/Jujuy</timezone>
     <timezone code="ar">America/Argentina/Tucuman</timezone>
     <timezone code="ar">America/Argentina/Catamarca</timezone>
     <timezone code="ar">America/Argentina/La_Rioja</timezone>
     <timezone code="ar">America/Argentina/San_Juan</timezone>
     <timezone code="ar">America/Argentina/Mendoza</timezone>
+    <timezone code="ar">America/Argentina/San_Luis</timezone>
     <timezone code="ar">America/Argentina/Rio_Gallegos</timezone>
     <timezone code="ar">America/Argentina/Ushuaia</timezone>
 
@@ -117,6 +121,9 @@
     <timezone code="au">Australia/Currie</timezone>
     <timezone code="au">Australia/Lindeman</timezone>
 
+    <!-- AUSTRALIA, 11:00 -->
+    <timezone code="au">Antarctica/Macquarie</timezone>
+
     <!-- AUSTRALIA, 10:30 -->
 
     <timezone code="au">Australia/Lord_Howe</timezone>
@@ -137,11 +144,11 @@
 
     <!-- ARUBA, -4:00 -->
 
-    <timezone code="aw">America/Aruba</timezone>
+    <timezone code="aw">America/Curacao</timezone>
 
     <!-- ALAND ISLANDS, 2:00 -->
 
-    <timezone code="ax">Europe/Mariehamn</timezone>
+    <timezone code="ax">Europe/Helsinki</timezone>
 
     <!-- AZERBAIJAN, 4:00 -->
 
@@ -149,7 +156,7 @@
 
     <!-- BOSNIA AND HERZEGOVINA, 1:00 -->
 
-    <timezone code="ba">Europe/Sarajevo</timezone>
+    <timezone code="ba">Europe/Belgrade</timezone>
 
     <!-- BARBADOS, -4:00 -->
 
@@ -165,7 +172,7 @@
 
     <!-- BURKINA FASO, 0:00 -->
 
-    <timezone code="bf">Africa/Ouagadougou</timezone>
+    <timezone code="bf">Africa/Abidjan</timezone>
 
     <!-- BULGARIA, 2:00 -->
 
@@ -173,15 +180,19 @@
 
     <!-- BAHRAIN, 3:00 -->
 
-    <timezone code="bh">Asia/Bahrain</timezone>
+    <timezone code="bh">Asia/Qatar</timezone>
 
     <!-- BURUNDI, 2:00 -->
 
-    <timezone code="bi">Africa/Bujumbura</timezone>
+    <timezone code="bi">Africa/Maputo</timezone>
 
     <!-- BENIN, 1:00 -->
 
-    <timezone code="bj">Africa/Porto-Novo</timezone>
+    <timezone code="bj">Africa/Lagos</timezone>
+
+    <!-- Saint Barthélemy, -4:00 -->
+
+    <timezone code="bl">America/Port_of_Spain</timezone>
 
     <!-- BERMUDA, -4:00 -->
 
@@ -195,6 +206,10 @@
 
     <timezone code="bo">America/La_Paz</timezone>
 
+    <!-- Caribbean Netherlands, -4:00 -->
+
+    <timezone code="bq">America/Curacao</timezone>
+
     <!-- BRAZIL, -2:00 -->
 
     <timezone code="br">America/Noronha</timezone>
@@ -208,6 +223,7 @@
     <timezone code="br">America/Araguaina</timezone>
     <timezone code="br">America/Maceio</timezone>
     <timezone code="br">America/Bahia</timezone>
+    <timezone code="br">America/Santarem</timezone>
 
     <!-- BRAZIL, -4:00 -->
 
@@ -216,6 +232,9 @@
     <timezone code="br">America/Cuiaba</timezone>
     <timezone code="br">America/Porto_Velho</timezone>
     <timezone code="br">America/Boa_Vista</timezone>
+
+    <!-- BRAZIL, -5:00 -->
+
     <timezone code="br">America/Eirunepe</timezone>
     <timezone code="br">America/Rio_Branco</timezone>
 
@@ -229,9 +248,9 @@
 
     <!-- BOTSWANA, 2:00 -->
 
-    <timezone code="bw">Africa/Gaborone</timezone>
+    <timezone code="bw">Africa/Maputo</timezone>
 
-    <!-- BELARUS, 2:00 -->
+    <!-- BELARUS, 3:00 -->
 
     <timezone code="by">Europe/Minsk</timezone>
 
@@ -254,12 +273,10 @@
     <!-- CANADA, -5:00 -->
 
     <timezone code="ca">America/Toronto</timezone>
-    <timezone code="ca">America/Montreal</timezone>
     <timezone code="ca">America/Nipigon</timezone>
     <timezone code="ca">America/Thunder_Bay</timezone>
     <timezone code="ca">America/Iqaluit</timezone>
     <timezone code="ca">America/Pangnirtung</timezone>
-    <timezone code="ca">America/Resolute</timezone>
     <timezone code="ca">America/Atikokan</timezone>
 
     <!-- CANADA, -6:00 -->
@@ -269,6 +286,7 @@
     <timezone code="ca">America/Rankin_Inlet</timezone>
     <timezone code="ca">America/Rainy_River</timezone>
     <timezone code="ca">America/Swift_Current</timezone>
+    <timezone code="ca">America/Resolute</timezone>
 
     <!-- CANADA, -7:00 -->
 
@@ -277,6 +295,8 @@
     <timezone code="ca">America/Yellowknife</timezone>
     <timezone code="ca">America/Inuvik</timezone>
     <timezone code="ca">America/Dawson_Creek</timezone>
+    <timezone code="ca">America/Creston</timezone>
+    <timezone code="ca">America/Fort_Nelson</timezone>
 
     <!-- CANADA, -8:00 -->
 
@@ -290,19 +310,19 @@
 
     <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 2:00 -->
 
-    <timezone code="cd">Africa/Lubumbashi</timezone>
+    <timezone code="cd">Africa/Maputo</timezone>
 
     <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 1:00 -->
 
-    <timezone code="cd">Africa/Kinshasa</timezone>
+    <timezone code="cd">Africa/Lagos</timezone>
 
     <!-- CENTRAL AFRICAN REPUBLIC, 1:00 -->
 
-    <timezone code="cf">Africa/Bangui</timezone>
+    <timezone code="cf">Africa/Lagos</timezone>
 
     <!-- CONGO, 1:00 -->
 
-    <timezone code="cg">Africa/Brazzaville</timezone>
+    <timezone code="cg">Africa/Lagos</timezone>
 
     <!-- SWITZERLAND, 1:00 -->
 
@@ -326,15 +346,15 @@
 
     <!-- CAMEROON, 1:00 -->
 
-    <timezone code="cm">Africa/Douala</timezone>
+    <timezone code="cm">Africa/Lagos</timezone>
 
     <!-- CHINA, 8:00 -->
 
     <timezone code="cn">Asia/Shanghai</timezone>
-    <timezone code="cn">Asia/Harbin</timezone>
-    <timezone code="cn">Asia/Chongqing</timezone>
+
+    <!-- CHINA, 6:00 -->
+
     <timezone code="cn">Asia/Urumqi</timezone>
-    <timezone code="cn">Asia/Kashgar</timezone>
 
     <!-- COLOMBIA, -5:00 -->
 
@@ -352,6 +372,10 @@
 
     <timezone code="cv">Atlantic/Cape_Verde</timezone>
 
+    <!-- Curaçao, -4:00 -->
+
+    <timezone code="cw">America/Curacao</timezone>
+
     <!-- CHRISTMAS ISLAND, 7:00 -->
 
     <timezone code="cx">Indian/Christmas</timezone>
@@ -367,10 +391,11 @@
     <!-- GERMANY, 1:00 -->
 
     <timezone code="de">Europe/Berlin</timezone>
+    <timezone code="de">Europe/Zurich</timezone>
 
     <!-- DJIBOUTI, 3:00 -->
 
-    <timezone code="dj">Africa/Djibouti</timezone>
+    <timezone code="dj">Africa/Nairobi</timezone>
 
     <!-- DENMARK, 1:00 -->
 
@@ -378,7 +403,7 @@
 
     <!-- DOMINICA, -4:00 -->
 
-    <timezone code="dm">America/Dominica</timezone>
+    <timezone code="dm">America/Port_of_Spain</timezone>
 
     <!-- DOMINICAN REPUBLIC, -4:00 -->
 
@@ -410,7 +435,7 @@
 
     <!-- ERITREA, 3:00 -->
 
-    <timezone code="er">Africa/Asmara</timezone>
+    <timezone code="er">Africa/Nairobi</timezone>
 
     <!-- SPAIN, 1:00 -->
 
@@ -423,7 +448,7 @@
 
     <!-- ETHIOPIA, 3:00 -->
 
-    <timezone code="et">Africa/Addis_Ababa</timezone>
+    <timezone code="et">Africa/Nairobi</timezone>
 
     <!-- FINLAND, 2:00 -->
 
@@ -433,7 +458,7 @@
 
     <timezone code="fj">Pacific/Fiji</timezone>
 
-    <!-- FALKLAND ISLANDS (MALVINAS), -4:00 -->
+    <!-- FALKLAND ISLANDS (MALVINAS), -3:00 -->
 
     <timezone code="fk">Atlantic/Stanley</timezone>
 
@@ -444,7 +469,7 @@
 
     <!-- MICRONESIA, FEDERATED STATES OF, 10:00 -->
 
-    <timezone code="fm">Pacific/Truk</timezone>
+    <timezone code="fm">Pacific/Chuuk</timezone>
 
     <!-- FAROE ISLANDS, 0:00 -->
 
@@ -456,7 +481,7 @@
 
     <!-- GABON, 1:00 -->
 
-    <timezone code="ga">Africa/Libreville</timezone>
+    <timezone code="ga">Africa/Lagos</timezone>
 
     <!-- UNITED KINGDOM, 0:00 -->
 
@@ -464,7 +489,7 @@
 
     <!-- GRENADA, -4:00 -->
 
-    <timezone code="gd">America/Grenada</timezone>
+    <timezone code="gd">America/Port_of_Spain</timezone>
 
     <!-- GEORGIA, 4:00 -->
 
@@ -476,7 +501,7 @@
 
     <!-- GUERNSEY, 0:00 -->
 
-    <timezone code="gg">Europe/Guernsey</timezone>
+    <timezone code="gg">Europe/London</timezone>
 
     <!-- GHANA, 0:00 -->
 
@@ -504,19 +529,19 @@
 
     <!-- GAMBIA, 0:00 -->
 
-    <timezone code="gm">Africa/Banjul</timezone>
+    <timezone code="gm">Africa/Abidjan</timezone>
 
     <!-- GUINEA, 0:00 -->
 
-    <timezone code="gn">Africa/Conakry</timezone>
+    <timezone code="gn">Africa/Abidjan</timezone>
 
     <!-- GUADELOUPE, -4:00 -->
 
-    <timezone code="gp">America/Guadeloupe</timezone>
+    <timezone code="gp">America/Port_of_Spain</timezone>
 
     <!-- EQUATORIAL GUINEA, 1:00 -->
 
-    <timezone code="gq">Africa/Malabo</timezone>
+    <timezone code="gq">Africa/Lagos</timezone>
 
     <!-- GREECE, 2:00 -->
 
@@ -552,7 +577,7 @@
 
     <!-- CROATIA, 1:00 -->
 
-    <timezone code="hr">Europe/Zagreb</timezone>
+    <timezone code="hr">Europe/Belgrade</timezone>
 
     <!-- HAITI, -5:00 -->
 
@@ -585,11 +610,11 @@
 
     <!-- ISLE OF MAN, 0:00 -->
 
-    <timezone code="im">Europe/Isle_of_Man</timezone>
+    <timezone code="im">Europe/London</timezone>
 
     <!-- INDIA, 5:30 -->
 
-    <timezone code="in">Asia/Calcutta</timezone>
+    <timezone code="in">Asia/Kolkata</timezone>
 
     <!-- BRITISH INDIAN OCEAN TERRITORY, 6:00 -->
 
@@ -613,7 +638,7 @@
 
     <!-- JERSEY, 0:00 -->
 
-    <timezone code="je">Europe/Jersey</timezone>
+    <timezone code="je">Europe/London</timezone>
 
     <!-- JAMAICA, -5:00 -->
 
@@ -637,7 +662,7 @@
 
     <!-- CAMBODIA, 7:00 -->
 
-    <timezone code="kh">Asia/Phnom_Penh</timezone>
+    <timezone code="kh">Asia/Bangkok</timezone>
 
     <!-- KIRIBATI, 14:00 -->
 
@@ -653,13 +678,13 @@
 
     <!-- COMOROS, 3:00 -->
 
-    <timezone code="km">Indian/Comoro</timezone>
+    <timezone code="km">Africa/Nairobi</timezone>
 
     <!-- SAINT KITTS AND NEVIS, -4:00 -->
 
-    <timezone code="kn">America/St_Kitts</timezone>
+    <timezone code="kn">America/Port_of_Spain</timezone>
 
-    <!-- KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF, 9:00 -->
+    <!-- KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF, 8:30 -->
 
     <timezone code="kp">Asia/Pyongyang</timezone>
 
@@ -669,11 +694,11 @@
 
     <!-- KUWAIT, 3:00 -->
 
-    <timezone code="kw">Asia/Kuwait</timezone>
+    <timezone code="kw">Asia/Riyadh</timezone>
 
     <!-- CAYMAN ISLANDS, -5:00 -->
 
-    <timezone code="ky">America/Cayman</timezone>
+    <timezone code="ky">America/Panama</timezone>
 
     <!-- KAZAKHSTAN, 6:00 -->
 
@@ -688,7 +713,7 @@
 
     <!-- LAO PEOPLE'S DEMOCRATIC REPUBLIC, 7:00 -->
 
-    <timezone code="la">Asia/Vientiane</timezone>
+    <timezone code="la">Asia/Bangkok</timezone>
 
     <!-- LEBANON, 2:00 -->
 
@@ -696,11 +721,11 @@
 
     <!-- SAINT LUCIA, -4:00 -->
 
-    <timezone code="lc">America/St_Lucia</timezone>
+    <timezone code="lc">America/Port_of_Spain</timezone>
 
     <!-- LIECHTENSTEIN, 1:00 -->
 
-    <timezone code="li">Europe/Vaduz</timezone>
+    <timezone code="li">Europe/Zurich</timezone>
 
     <!-- SRI LANKA, 5:30 -->
 
@@ -712,7 +737,7 @@
 
     <!-- LESOTHO, 2:00 -->
 
-    <timezone code="ls">Africa/Maseru</timezone>
+    <timezone code="ls">Africa/Johannesburg</timezone>
 
     <!-- LITHUANIA, 2:00 -->
 
@@ -744,11 +769,15 @@
 
     <!-- MONTENEGRO, 1:00 -->
 
-    <timezone code="me">Europe/Podgorica</timezone>
+    <timezone code="me">Europe/Belgrade</timezone>
+
+    <!-- Collectivity of Saint Martin, -4:00 -->
+
+    <timezone code="mf">America/Port_of_Spain</timezone>
 
     <!-- MADAGASCAR, 3:00 -->
 
-    <timezone code="mg">Indian/Antananarivo</timezone>
+    <timezone code="mg">Africa/Nairobi</timezone>
 
     <!-- MARSHALL ISLANDS, 12:00 -->
 
@@ -757,15 +786,15 @@
 
     <!-- MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF, 1:00 -->
 
-    <timezone code="mk">Europe/Skopje</timezone>
+    <timezone code="mk">Europe/Belgrade</timezone>
 
     <!-- MALI, 0:00 -->
 
-    <timezone code="ml">Africa/Bamako</timezone>
+    <timezone code="ml">Africa/Abidjan</timezone>
 
     <!-- MYANMAR, 6:30 -->
 
-    <timezone code="mm">Asia/Rangoon</timezone>
+    <timezone code="mm">Asia/Yangon</timezone>
 
     <!-- MONGOLIA, 8:00 -->
 
@@ -782,7 +811,7 @@
 
     <!-- NORTHERN MARIANA ISLANDS, 10:00 -->
 
-    <timezone code="mp">Pacific/Saipan</timezone>
+    <timezone code="mp">Pacific/Guam</timezone>
 
     <!-- MARTINIQUE, -4:00 -->
 
@@ -790,11 +819,11 @@
 
     <!-- MAURITANIA, 0:00 -->
 
-    <timezone code="mr">Africa/Nouakchott</timezone>
+    <timezone code="mr">Africa/Abidjan</timezone>
 
     <!-- MONTSERRAT, -4:00 -->
 
-    <timezone code="ms">America/Montserrat</timezone>
+    <timezone code="ms">America/Port_of_Spain</timezone>
 
     <!-- MALTA, 1:00 -->
 
@@ -810,20 +839,26 @@
 
     <!-- MALAWI, 2:00 -->
 
-    <timezone code="mw">Africa/Blantyre</timezone>
+    <timezone code="mw">Africa/Maputo</timezone>
 
     <!-- MEXICO, -6:00 -->
 
     <timezone code="mx">America/Mexico_City</timezone>
-    <timezone code="mx">America/Cancun</timezone>
     <timezone code="mx">America/Merida</timezone>
     <timezone code="mx">America/Monterrey</timezone>
+    <timezone code="mx">America/Matamoros</timezone>
+    <timezone code="mx">America/Bahia_Banderas</timezone>
+
+    <!-- MEXICO, -5:00 -->
+
+    <timezone code="mx">America/Cancun</timezone>
 
     <!-- MEXICO, -7:00 -->
 
     <timezone code="mx">America/Chihuahua</timezone>
     <timezone code="mx">America/Hermosillo</timezone>
     <timezone code="mx">America/Mazatlan</timezone>
+    <timezone code="mx">America/Ojinaga</timezone>
 
     <!-- MEXICO, -8:00 -->
 
@@ -848,7 +883,7 @@
 
     <!-- NIGER, 1:00 -->
 
-    <timezone code="ne">Africa/Niamey</timezone>
+    <timezone code="ne">Africa/Lagos</timezone>
 
     <!-- NORFOLK ISLAND, 11:30 -->
 
@@ -892,7 +927,7 @@
 
     <!-- OMAN, 4:00 -->
 
-    <timezone code="om">Asia/Muscat</timezone>
+    <timezone code="om">Asia/Dubai</timezone>
 
     <!-- PANAMA, -5:00 -->
 
@@ -918,6 +953,10 @@
 
     <timezone code="pg">Pacific/Port_Moresby</timezone>
 
+    <!-- PAPUA NEW GUINEA, 11:00 -->
+
+    <timezone code="pg">Pacific/Bougainville</timezone>
+
     <!-- PHILIPPINES, 8:00 -->
 
     <timezone code="ph">Asia/Manila</timezone>
@@ -945,6 +984,7 @@
     <!-- PALESTINE, 2:00 -->
 
     <timezone code="ps">Asia/Gaza</timezone>
+    <timezone code="ps">Asia/Hebron</timezone>
 
     <!-- PORTUGAL, 0:00 -->
 
@@ -987,15 +1027,19 @@
     <!-- RUSSIAN FEDERATION, 11:00 -->
 
     <timezone code="ru">Asia/Magadan</timezone>
+    <timezone code="ru">Asia/Sakhalin</timezone>
+    <timezone code="ru">Asia/Srednekolymsk</timezone>
 
     <!-- RUSSIAN FEDERATION, 10:00 -->
 
     <timezone code="ru">Asia/Vladivostok</timezone>
-    <timezone code="ru">Asia/Sakhalin</timezone>
+    <timezone code="ru">Asia/Ust-Nera</timezone>
 
     <!-- RUSSIAN FEDERATION, 9:00 -->
 
     <timezone code="ru">Asia/Yakutsk</timezone>
+    <timezone code="ru">Asia/Chita</timezone>
+    <timezone code="ru">Asia/Khandyga</timezone>
 
     <!-- RUSSIAN FEDERATION, 8:00 -->
 
@@ -1004,10 +1048,13 @@
     <!-- RUSSIAN FEDERATION, 7:00 -->
 
     <timezone code="ru">Asia/Krasnoyarsk</timezone>
+    <timezone code="ru">Asia/Novosibirsk</timezone>
+    <timezone code="ru">Asia/Barnaul</timezone>
+    <timezone code="ru">Asia/Novokuznetsk</timezone>
+    <timezone code="ru">Asia/Tomsk</timezone>
 
     <!-- RUSSIAN FEDERATION, 6:00 -->
 
-    <timezone code="ru">Asia/Novosibirsk</timezone>
     <timezone code="ru">Asia/Omsk</timezone>
 
     <!-- RUSSIAN FEDERATION, 5:00 -->
@@ -1017,11 +1064,15 @@
     <!-- RUSSIAN FEDERATION, 4:00 -->
 
     <timezone code="ru">Europe/Samara</timezone>
+    <timezone code="ru">Europe/Astrakhan</timezone>
+    <timezone code="ru">Europe/Ulyanovsk</timezone>
 
     <!-- RUSSIAN FEDERATION, 3:00 -->
 
     <timezone code="ru">Europe/Moscow</timezone>
     <timezone code="ru">Europe/Volgograd</timezone>
+    <timezone code="ru">Europe/Kirov</timezone>
+    <timezone code="ru">Europe/Simferopol</timezone>
 
     <!-- RUSSIAN FEDERATION, 2:00 -->
 
@@ -1029,7 +1080,7 @@
 
     <!-- RWANDA, 2:00 -->
 
-    <timezone code="rw">Africa/Kigali</timezone>
+    <timezone code="rw">Africa/Maputo</timezone>
 
     <!-- SAUDI ARABIA, 3:00 -->
 
@@ -1057,57 +1108,65 @@
 
     <!-- SAINT HELENA, 0:00 -->
 
-    <timezone code="sh">Atlantic/St_Helena</timezone>
+    <timezone code="sh">Africa/Abidjan</timezone>
 
     <!-- SLOVENIA, 1:00 -->
 
-    <timezone code="si">Europe/Ljubljana</timezone>
+    <timezone code="si">Europe/Belgrade</timezone>
 
     <!-- SVALBARD AND JAN MAYEN, 1:00 -->
 
-    <timezone code="sj">Arctic/Longyearbyen</timezone>
+    <timezone code="sj">Europe/Oslo</timezone>
 
     <!-- SLOVAKIA, 1:00 -->
 
-    <timezone code="sk">Europe/Bratislava</timezone>
+    <timezone code="sk">Europe/Prague</timezone>
 
     <!-- SIERRA LEONE, 0:00 -->
 
-    <timezone code="sl">Africa/Freetown</timezone>
+    <timezone code="sl">Africa/Abidjan</timezone>
 
     <!-- SAN MARINO, 1:00 -->
 
-    <timezone code="sm">Europe/San_Marino</timezone>
+    <timezone code="sm">Europe/Rome</timezone>
 
     <!-- SENEGAL, 0:00 -->
 
-    <timezone code="sn">Africa/Dakar</timezone>
+    <timezone code="sn">Africa/Abidjan</timezone>
 
     <!-- SOMALIA, 3:00 -->
 
-    <timezone code="so">Africa/Mogadishu</timezone>
+    <timezone code="so">Africa/Nairobi</timezone>
 
     <!-- SURINAME, -3:00 -->
 
     <timezone code="sr">America/Paramaribo</timezone>
 
+    <!-- South Sudan, 3:00 -->
+
+    <timezone code="ss">Africa/Khartoum</timezone>
+
     <!-- SAO TOME AND PRINCIPE, 0:00 -->
 
-    <timezone code="st">Africa/Sao_Tome</timezone>
+    <timezone code="st">Africa/Abidjan</timezone>
 
     <!-- EL SALVADOR, -6:00 -->
 
     <timezone code="sv">America/El_Salvador</timezone>
 
+    <!-- Sint Maarten, -4:00 -->
+
+    <timezone code="sx">America/Curacao</timezone>
+
     <!-- SYRIAN ARAB REPUBLIC, 2:00 -->
 
     <timezone code="sy">Asia/Damascus</timezone>
 
     <!-- SWAZILAND, 2:00 -->
 
-    <timezone code="sz">Africa/Mbabane</timezone>
+    <timezone code="sz">Africa/Johannesburg</timezone>
 
-    <!-- TURKS AND CAICOS ISLANDS, -5:00 -->
+    <!-- TURKS AND CAICOS ISLANDS, -4:00 -->
 
     <timezone code="tc">America/Grand_Turk</timezone>
 
@@ -1119,9 +1178,13 @@
 
     <timezone code="tf">Indian/Kerguelen</timezone>
 
+    <!-- FRENCH SOUTHERN TERRITORIES, 4:00 -->
+
+    <timezone code="tf">Indian/Reunion</timezone>
+
     <!-- TOGO, 0:00 -->
 
-    <timezone code="tg">Africa/Lome</timezone>
+    <timezone code="tg">Africa/Abidjan</timezone>
 
     <!-- THAILAND, 7:00 -->
 
@@ -1131,7 +1194,7 @@
 
     <timezone code="tj">Asia/Dushanbe</timezone>
 
-    <!-- TOKELAU, -10:00 -->
+    <!-- TOKELAU, +13:00 -->
 
     <timezone code="tk">Pacific/Fakaofo</timezone>
 
@@ -1151,7 +1214,7 @@
 
     <timezone code="to">Pacific/Tongatapu</timezone>
 
-    <!-- TURKEY, 2:00 -->
+    <!-- TURKEY, 3:00 -->
 
     <timezone code="tr">Europe/Istanbul</timezone>
 
@@ -1169,18 +1232,17 @@
 
     <!-- TANZANIA, UNITED REPUBLIC OF, 3:00 -->
 
-    <timezone code="tz">Africa/Dar_es_Salaam</timezone>
+    <timezone code="tz">Africa/Nairobi</timezone>
 
     <!-- UKRAINE, 2:00 -->
 
     <timezone code="ua">Europe/Kiev</timezone>
     <timezone code="ua">Europe/Uzhgorod</timezone>
     <timezone code="ua">Europe/Zaporozhye</timezone>
-    <timezone code="ua">Europe/Simferopol</timezone>
 
     <!-- UGANDA, 3:00 -->
 
-    <timezone code="ug">Africa/Kampala</timezone>
+    <timezone code="ug">Africa/Nairobi</timezone>
 
     <!-- UNITED STATES MINOR OUTLYING ISLANDS, 12:00 -->
 
@@ -1188,11 +1250,11 @@
 
     <!-- UNITED STATES MINOR OUTLYING ISLANDS, -10:00 -->
 
-    <timezone code="um">Pacific/Johnston</timezone>
+    <timezone code="um">Pacific/Honolulu</timezone>
 
     <!-- UNITED STATES MINOR OUTLYING ISLANDS, -11:00 -->
 
-    <timezone code="um">Pacific/Midway</timezone>
+    <timezone code="um">Pacific/Pago_Pago</timezone>
 
     <!-- UNITED STATES, -5:00 -->
 
@@ -1214,12 +1276,13 @@
     <timezone code="us">America/Menominee</timezone>
     <timezone code="us">America/North_Dakota/Center</timezone>
     <timezone code="us">America/North_Dakota/New_Salem</timezone>
+    <timezone code="us">America/Indiana/Tell_City</timezone>
+    <timezone code="us">America/North_Dakota/Beulah</timezone>
 
     <!-- UNITED STATES, -7:00 -->
 
     <timezone code="us">America/Denver</timezone>
     <timezone code="us">America/Boise</timezone>
-    <timezone code="us">America/Shiprock</timezone>
     <timezone code="us">America/Phoenix</timezone>
 
     <!-- UNITED STATES, -8:00 -->
@@ -1232,6 +1295,8 @@
     <timezone code="us">America/Juneau</timezone>
     <timezone code="us">America/Yakutat</timezone>
     <timezone code="us">America/Nome</timezone>
+    <timezone code="us">America/Metlakatla</timezone>
+    <timezone code="us">America/Sitka</timezone>
 
     <!-- UNITED STATES, -10:00 -->
 
@@ -1249,27 +1314,28 @@
 
     <!-- HOLY SEE (VATICAN CITY STATE), 1:00 -->
 
-    <timezone code="va">Europe/Vatican</timezone>
+    <timezone code="va">Europe/Rome</timezone>
 
     <!-- SAINT VINCENT AND THE GRENADINES, -4:00 -->
 
-    <timezone code="vc">America/St_Vincent</timezone>
+    <timezone code="vc">America/Port_of_Spain</timezone>
 
-    <!-- VENEZUELA, -4:30 -->
+    <!-- VENEZUELA, -4:00 -->
 
     <timezone code="ve">America/Caracas</timezone>
 
     <!-- VIRGIN ISLANDS, BRITISH, -4:00 -->
 
-    <timezone code="vg">America/Tortola</timezone>
+    <timezone code="vg">America/Port_of_Spain</timezone>
 
     <!-- VIRGIN ISLANDS, U.S., -4:00 -->
 
-    <timezone code="vi">America/St_Thomas</timezone>
+    <timezone code="vi">America/Port_of_Spain</timezone>
 
     <!-- VIET NAM, 7:00 -->
 
-    <timezone code="vn">Asia/Saigon</timezone>
+    <timezone code="vn">Asia/Ho_Chi_Minh</timezone>
+    <timezone code="vn">Asia/Bangkok</timezone>
 
     <!-- VANUATU, 11:00 -->
 
@@ -1279,17 +1345,17 @@
 
     <timezone code="wf">Pacific/Wallis</timezone>
 
-    <!-- SAMOA, -11:00 -->
+    <!-- SAMOA, 13:00 -->
 
     <timezone code="ws">Pacific/Apia</timezone>
 
     <!-- YEMEN, 3:00 -->
 
-    <timezone code="ye">Asia/Aden</timezone>
+    <timezone code="ye">Asia/Riyadh</timezone>
 
     <!-- MAYOTTE, 3:00 -->
 
-    <timezone code="yt">Indian/Mayotte</timezone>
+    <timezone code="yt">Africa/Nairobi</timezone>
 
     <!-- SOUTH AFRICA, 2:00 -->
 
@@ -1297,9 +1363,9 @@
 
     <!-- ZAMBIA, 2:00 -->
 
-    <timezone code="zm">Africa/Lusaka</timezone>
+    <timezone code="zm">Africa/Maputo</timezone>
 
     <!-- ZIMBABWE, 2:00 -->
 
-    <timezone code="zw">Africa/Harare</timezone>
+    <timezone code="zw">Africa/Maputo</timezone>
 </timezones>
diff --git a/core/tests/benchmarks/README b/core/tests/benchmarks/README
index 0a41bcc..e4d2dce 100644
--- a/core/tests/benchmarks/README
+++ b/core/tests/benchmarks/README
@@ -5,4 +5,21 @@
 http://code.google.com/p/caliper/
 http://code.google.com/p/vogar/
 
-$ vogar --benchmark path/to/Benchmark.java
+-------------------------
+
+Quick Command Line Reference:
+
+# Build vogar and dependencies.
+$> mmma -j32 external/vogar
+
+# First make sure art has permissions to dalvik-cache, otherwise it will run slower with interpreter.
+$> adb root
+
+# Run vogar in benchmark mode, telling it to use app_process (not dalvikvm which is default)
+# Otherwise you will likely crash with UnsatisfiedLinkError despite having correct JNI code.
+
+$> vogar --mode app_process --benchmark path/to/Benchmark.java
+
+# Sometimes your benchmarks might time out, if so increase the timeout:
+# (--timeout goes to vogar, and --time-limit goes to caliper)
+$> vogar --timeout 1000 --mode app_process --benchmark path/to/Benchmark -- --time-limit 9999s
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk
index 9e45d09..d7b38e8 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib/Android.mk
@@ -19,8 +19,7 @@
 LOCAL_SRC_FILES := \
     com_android_frameworks_coretests_JNITest.cpp
 
-LOCAL_SHARED_LIBRARIES := \
-    libnativehelper
+LOCAL_SDK_VERSION := 16
 
 LOCAL_CFLAGS += -Wall -Werror
 
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index 8d91192..0cf3a84 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -14,41 +14,23 @@
  * limitations under the License.
  */
 
-#include "nativehelper/JNIHelp.h"
+#include <jni.h>
 
-namespace android {
-
-static jint checkFunction(JNIEnv*, jclass) {
+extern "C" JNIEXPORT
+jint JNICALL Java_com_android_frameworks_coretests_JNITests_checkFunction(JNIEnv*, jclass) {
     return 1;
 }
 
-static const JNINativeMethod sMethods[] = {
-    /* name, signature, funcPtr */
-    { "checkFunction", "()I", (void*) checkFunction },
-};
-
-int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods,
-            NELEM(sMethods));
-}
-
-}
-
 /*
  * JNI Initialization
  */
 jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) {
     JNIEnv *e;
-    int status;
 
     // Check JNI version
     if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) {
         return JNI_ERR;
     }
 
-    if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) {
-        return JNI_ERR;
-    }
-
     return JNI_VERSION_1_6;
 }
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
deleted file mode 100644
index 2370627..0000000
--- a/core/tests/coretests/src/android/util/TimeUtilsTest.java
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Copyright (C) 2008 Google Inc.
- *
- * 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 android.util;
-
-import junit.framework.TestCase;
-
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * TimeUtilsTest tests the time zone guesser.
- */
-public class TimeUtilsTest extends TestCase {
-    public void testMainstream() throws Exception {
-        String[] mainstream = new String[] {
-            "America/New_York", // Eastern
-            "America/Chicago", // Central
-            "America/Denver", // Mountain
-            "America/Los_Angeles", // Pacific
-            "America/Anchorage", // Alaska
-            "Pacific/Honolulu", // Hawaii, no DST
-        };
-
-        for (String name : mainstream) {
-            TimeZone tz = TimeZone.getTimeZone(name);
-            Calendar c = Calendar.getInstance(tz);
-            TimeZone guess;
-
-            c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
-            guess = guess(c, "us");
-            assertEquals(name, guess.getID());
-
-            c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
-            guess = guess(c, "us");
-            assertEquals(name, guess.getID());
-        }
-    }
-
-    public void testWeird() throws Exception {
-        String[] weird = new String[] {
-            "America/Phoenix", // Mountain, no DST
-            "America/Adak", // Same as Hawaii, but with DST
-        };
-
-        for (String name : weird) {
-            TimeZone tz = TimeZone.getTimeZone(name);
-            Calendar c = Calendar.getInstance(tz);
-            TimeZone guess;
-
-            c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
-            guess = guess(c, "us");
-            assertEquals(name, guess.getID());
-        }
-    }
-
-    public void testOld() throws Exception {
-        String[] old = new String[] {
-            "America/Indiana/Indianapolis", // Eastern, formerly no DST
-        };
-
-        for (String name : old) {
-            TimeZone tz = TimeZone.getTimeZone(name);
-            Calendar c = Calendar.getInstance(tz);
-            TimeZone guess;
-
-            c.set(2005, Calendar.OCTOBER, 20, 12, 00, 00);
-            guess = guess(c, "us");
-            assertEquals(name, guess.getID());
-        }
-    }
-
-    public void testWorld() throws Exception {
-        String[] world = new String[] {
-            "ad", "Europe/Andorra",
-            "ae", "Asia/Dubai",
-            "af", "Asia/Kabul",
-            "ag", "America/Antigua",
-            "ai", "America/Anguilla",
-            "al", "Europe/Tirane",
-            "am", "Asia/Yerevan",
-            "an", "America/Curacao",
-            "ao", "Africa/Luanda",
-            "aq", "Antarctica/McMurdo",
-            "aq", "Antarctica/DumontDUrville",
-            "aq", "Antarctica/Casey",
-            "aq", "Antarctica/Davis",
-            "aq", "Antarctica/Mawson",
-            "aq", "Antarctica/Syowa",
-            "aq", "Antarctica/Rothera",
-            "aq", "Antarctica/Palmer",
-            "ar", "America/Argentina/Buenos_Aires",
-            "as", "Pacific/Pago_Pago",
-            "at", "Europe/Vienna",
-            "au", "Australia/Sydney",
-            "au", "Australia/Adelaide",
-            "au", "Australia/Perth",
-            "au", "Australia/Eucla",
-            "aw", "America/Aruba",
-            "ax", "Europe/Mariehamn",
-            "az", "Asia/Baku",
-            "ba", "Europe/Sarajevo",
-            "bb", "America/Barbados",
-            "bd", "Asia/Dhaka",
-            "be", "Europe/Brussels",
-            "bf", "Africa/Ouagadougou",
-            "bg", "Europe/Sofia",
-            "bh", "Asia/Bahrain",
-            "bi", "Africa/Bujumbura",
-            "bj", "Africa/Porto-Novo",
-            "bm", "Atlantic/Bermuda",
-            "bn", "Asia/Brunei",
-            "bo", "America/La_Paz",
-            "br", "America/Noronha",
-            "br", "America/Sao_Paulo",
-            "br", "America/Manaus",
-            "bs", "America/Nassau",
-            "bt", "Asia/Thimphu",
-            "bw", "Africa/Gaborone",
-            "by", "Europe/Minsk",
-            "bz", "America/Belize",
-            "ca", "America/St_Johns",
-            "ca", "America/Halifax",
-            "ca", "America/Toronto",
-            "ca", "America/Winnipeg",
-            "ca", "America/Edmonton",
-            "ca", "America/Vancouver",
-            "cc", "Indian/Cocos",
-            "cd", "Africa/Lubumbashi",
-            "cd", "Africa/Kinshasa",
-            "cf", "Africa/Bangui",
-            "cg", "Africa/Brazzaville",
-            "ch", "Europe/Zurich",
-            "ci", "Africa/Abidjan",
-            "ck", "Pacific/Rarotonga",
-            "cl", "America/Santiago",
-            "cl", "Pacific/Easter",
-            "cm", "Africa/Douala",
-            "cn", "Asia/Shanghai",
-            "co", "America/Bogota",
-            "cr", "America/Costa_Rica",
-            "cu", "America/Havana",
-            "cv", "Atlantic/Cape_Verde",
-            "cx", "Indian/Christmas",
-            "cy", "Asia/Nicosia",
-            "cz", "Europe/Prague",
-            "de", "Europe/Berlin",
-            "dj", "Africa/Djibouti",
-            "dk", "Europe/Copenhagen",
-            "dm", "America/Dominica",
-            "do", "America/Santo_Domingo",
-            "dz", "Africa/Algiers",
-            "ec", "America/Guayaquil",
-            "ec", "Pacific/Galapagos",
-            "ee", "Europe/Tallinn",
-            "eg", "Africa/Cairo",
-            "eh", "Africa/El_Aaiun",
-            "er", "Africa/Asmara",
-            "es", "Europe/Madrid",
-            "es", "Atlantic/Canary",
-            "et", "Africa/Addis_Ababa",
-            "fi", "Europe/Helsinki",
-            "fj", "Pacific/Fiji",
-            "fk", "Atlantic/Stanley",
-            "fm", "Pacific/Ponape",
-            "fm", "Pacific/Truk",
-            "fo", "Atlantic/Faroe",
-            "fr", "Europe/Paris",
-            "ga", "Africa/Libreville",
-            "gb", "Europe/London",
-            "gd", "America/Grenada",
-            "ge", "Asia/Tbilisi",
-            "gf", "America/Cayenne",
-            "gg", "Europe/Guernsey",
-            "gh", "Africa/Accra",
-            "gi", "Europe/Gibraltar",
-            "gl", "America/Danmarkshavn",
-            "gl", "America/Scoresbysund",
-            "gl", "America/Godthab",
-            "gl", "America/Thule",
-            "gm", "Africa/Banjul",
-            "gn", "Africa/Conakry",
-            "gp", "America/Guadeloupe",
-            "gq", "Africa/Malabo",
-            "gr", "Europe/Athens",
-            "gs", "Atlantic/South_Georgia",
-            "gt", "America/Guatemala",
-            "gu", "Pacific/Guam",
-            "gw", "Africa/Bissau",
-            "gy", "America/Guyana",
-            "hk", "Asia/Hong_Kong",
-            "hn", "America/Tegucigalpa",
-            "hr", "Europe/Zagreb",
-            "ht", "America/Port-au-Prince",
-            "hu", "Europe/Budapest",
-            "id", "Asia/Jayapura",
-            "id", "Asia/Makassar",
-            "id", "Asia/Jakarta",
-            "ie", "Europe/Dublin",
-            "il", "Asia/Jerusalem",
-            "im", "Europe/Isle_of_Man",
-            "in", "Asia/Calcutta",
-            "io", "Indian/Chagos",
-            "iq", "Asia/Baghdad",
-            "ir", "Asia/Tehran",
-            "is", "Atlantic/Reykjavik",
-            "it", "Europe/Rome",
-            "je", "Europe/Jersey",
-            "jm", "America/Jamaica",
-            "jo", "Asia/Amman",
-            "jp", "Asia/Tokyo",
-            "ke", "Africa/Nairobi",
-            "kg", "Asia/Bishkek",
-            "kh", "Asia/Phnom_Penh",
-            "ki", "Pacific/Kiritimati",
-            "ki", "Pacific/Enderbury",
-            "ki", "Pacific/Tarawa",
-            "km", "Indian/Comoro",
-            "kn", "America/St_Kitts",
-            "kp", "Asia/Pyongyang",
-            "kr", "Asia/Seoul",
-            "kw", "Asia/Kuwait",
-            "ky", "America/Cayman",
-            "kz", "Asia/Almaty",
-            "kz", "Asia/Aqtau",
-            "la", "Asia/Vientiane",
-            "lb", "Asia/Beirut",
-            "lc", "America/St_Lucia",
-            "li", "Europe/Vaduz",
-            "lk", "Asia/Colombo",
-            "lr", "Africa/Monrovia",
-            "ls", "Africa/Maseru",
-            "lt", "Europe/Vilnius",
-            "lu", "Europe/Luxembourg",
-            "lv", "Europe/Riga",
-            "ly", "Africa/Tripoli",
-            "ma", "Africa/Casablanca",
-            "mc", "Europe/Monaco",
-            "md", "Europe/Chisinau",
-            "me", "Europe/Podgorica",
-            "mg", "Indian/Antananarivo",
-            "mh", "Pacific/Majuro",
-            "mk", "Europe/Skopje",
-            "ml", "Africa/Bamako",
-            "mm", "Asia/Rangoon",
-            "mn", "Asia/Choibalsan",
-            "mn", "Asia/Hovd",
-            "mo", "Asia/Macau",
-            "mp", "Pacific/Saipan",
-            "mq", "America/Martinique",
-            "mr", "Africa/Nouakchott",
-            "ms", "America/Montserrat",
-            "mt", "Europe/Malta",
-            "mu", "Indian/Mauritius",
-            "mv", "Indian/Maldives",
-            "mw", "Africa/Blantyre",
-            "mx", "America/Mexico_City",
-            "mx", "America/Chihuahua",
-            "mx", "America/Tijuana",
-            "my", "Asia/Kuala_Lumpur",
-            "mz", "Africa/Maputo",
-            "na", "Africa/Windhoek",
-            "nc", "Pacific/Noumea",
-            "ne", "Africa/Niamey",
-            "nf", "Pacific/Norfolk",
-            "ng", "Africa/Lagos",
-            "ni", "America/Managua",
-            "nl", "Europe/Amsterdam",
-            "no", "Europe/Oslo",
-            "np", "Asia/Katmandu",
-            "nr", "Pacific/Nauru",
-            "nu", "Pacific/Niue",
-            "nz", "Pacific/Auckland",
-            "nz", "Pacific/Chatham",
-            "om", "Asia/Muscat",
-            "pa", "America/Panama",
-            "pe", "America/Lima",
-            "pf", "Pacific/Gambier",
-            "pf", "Pacific/Marquesas",
-            "pf", "Pacific/Tahiti",
-            "pg", "Pacific/Port_Moresby",
-            "ph", "Asia/Manila",
-            "pk", "Asia/Karachi",
-            "pl", "Europe/Warsaw",
-            "pm", "America/Miquelon",
-            "pn", "Pacific/Pitcairn",
-            "pr", "America/Puerto_Rico",
-            "ps", "Asia/Gaza",
-            "pt", "Europe/Lisbon",
-            "pt", "Atlantic/Azores",
-            "pw", "Pacific/Palau",
-            "py", "America/Asuncion",
-            "qa", "Asia/Qatar",
-            "re", "Indian/Reunion",
-            "ro", "Europe/Bucharest",
-            "rs", "Europe/Belgrade",
-            "ru", "Asia/Kamchatka",
-            "ru", "Asia/Magadan",
-            "ru", "Asia/Vladivostok",
-            "ru", "Asia/Yakutsk",
-            "ru", "Asia/Irkutsk",
-            "ru", "Asia/Krasnoyarsk",
-            "ru", "Asia/Novosibirsk",
-            "ru", "Asia/Yekaterinburg",
-            "ru", "Europe/Samara",
-            "ru", "Europe/Moscow",
-            "ru", "Europe/Kaliningrad",
-            "rw", "Africa/Kigali",
-            "sa", "Asia/Riyadh",
-            "sb", "Pacific/Guadalcanal",
-            "sc", "Indian/Mahe",
-            "sd", "Africa/Khartoum",
-            "se", "Europe/Stockholm",
-            "sg", "Asia/Singapore",
-            "sh", "Atlantic/St_Helena",
-            "si", "Europe/Ljubljana",
-            "sj", "Arctic/Longyearbyen",
-            "sk", "Europe/Bratislava",
-            "sl", "Africa/Freetown",
-            "sm", "Europe/San_Marino",
-            "sn", "Africa/Dakar",
-            "so", "Africa/Mogadishu",
-            "sr", "America/Paramaribo",
-            "st", "Africa/Sao_Tome",
-            "sv", "America/El_Salvador",
-            "sy", "Asia/Damascus",
-            "sz", "Africa/Mbabane",
-            "tc", "America/Grand_Turk",
-            "td", "Africa/Ndjamena",
-            "tf", "Indian/Kerguelen",
-            "tg", "Africa/Lome",
-            "th", "Asia/Bangkok",
-            "tj", "Asia/Dushanbe",
-            "tk", "Pacific/Fakaofo",
-            "tl", "Asia/Dili",
-            "tm", "Asia/Ashgabat",
-            "tn", "Africa/Tunis",
-            "to", "Pacific/Tongatapu",
-            "tr", "Europe/Istanbul",
-            "tt", "America/Port_of_Spain",
-            "tv", "Pacific/Funafuti",
-            "tw", "Asia/Taipei",
-            "tz", "Africa/Dar_es_Salaam",
-            "ua", "Europe/Kiev",
-            "ug", "Africa/Kampala",
-            "um", "Pacific/Wake",
-            "um", "Pacific/Johnston",
-            "um", "Pacific/Midway",
-            "us", "America/New_York",
-            "us", "America/Chicago",
-            "us", "America/Denver",
-            "us", "America/Los_Angeles",
-            "us", "America/Anchorage",
-            "us", "Pacific/Honolulu",
-            "uy", "America/Montevideo",
-            "uz", "Asia/Tashkent",
-            "va", "Europe/Vatican",
-            "vc", "America/St_Vincent",
-            "ve", "America/Caracas",
-            "vg", "America/Tortola",
-            "vi", "America/St_Thomas",
-            "vn", "Asia/Saigon",
-            "vu", "Pacific/Efate",
-            "wf", "Pacific/Wallis",
-            "ws", "Pacific/Apia",
-            "ye", "Asia/Aden",
-            "yt", "Indian/Mayotte",
-            "za", "Africa/Johannesburg",
-            "zm", "Africa/Lusaka",
-            "zw", "Africa/Harare",
-        };
-
-        for (int i = 0; i < world.length; i += 2) {
-            String country = world[i];
-            String name = world[i + 1];
-
-            TimeZone tz = TimeZone.getTimeZone(name);
-            Calendar c = Calendar.getInstance(tz);
-            TimeZone guess;
-
-            c.set(2009, Calendar.JULY, 20, 12, 00, 00);
-            guess = guess(c, country);
-            assertEquals(name, guess.getID());
-
-            c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
-            guess = guess(c, country);
-            assertEquals(name, guess.getID());
-        }
-    }
-
-    public void testWorldWeird() throws Exception {
-        String[] world = new String[] {
-            // Distinguisable from Sydney only when DST not in effect
-            "au", "Australia/Lord_Howe",
-        };
-
-        for (int i = 0; i < world.length; i += 2) {
-            String country = world[i];
-            String name = world[i + 1];
-
-            TimeZone tz = TimeZone.getTimeZone(name);
-            Calendar c = Calendar.getInstance(tz);
-            TimeZone guess;
-
-            c.set(2009, Calendar.JULY, 20, 12, 00, 00);
-            guess = guess(c, country);
-            assertEquals(name, guess.getID());
-        }
-    }
-
-    private static TimeZone guess(Calendar c, String country) {
-        return TimeUtils.getTimeZone(c.get(c.ZONE_OFFSET) + c.get(c.DST_OFFSET),
-                                     c.get(c.DST_OFFSET) != 0,
-                                     c.getTimeInMillis(),
-                                     country);
-    }
-
-    public void testFormatDuration() {
-        assertFormatDuration("0", 0);
-        assertFormatDuration("-1ms", -1);
-        assertFormatDuration("+1ms", 1);
-        assertFormatDuration("+10ms", 10);
-        assertFormatDuration("+100ms", 100);
-        assertFormatDuration("+101ms", 101);
-        assertFormatDuration("+330ms", 330);
-        assertFormatDuration("+1s0ms", 1000);
-        assertFormatDuration("+1s330ms", 1330);
-        assertFormatDuration("+10s24ms", 10024);
-        assertFormatDuration("+1m0s30ms", 60030);
-        assertFormatDuration("+1h0m0s30ms", 3600030);
-        assertFormatDuration("+1d0h0m0s30ms", 86400030);
-    }
-
-    public void testFormatHugeDuration() {
-        assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
-        assertFormatDuration("-15542d1h11m11s555ms", -1342833071555L);
-    }
-
-    private void assertFormatDuration(String expected, long duration) {
-        StringBuilder sb = new StringBuilder();
-        TimeUtils.formatDuration(duration, sb);
-        assertEquals("formatDuration(" + duration + ")", expected, sb.toString());
-    }
-}
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 6bbfcd2..76d521d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -49,6 +49,8 @@
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 LOCAL_SRC_FILES:= $(hostSources)
 LOCAL_C_INCLUDES := external/zlib
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -71,6 +73,8 @@
     libutils \
     libz
 
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 8ea25d6..7a10343 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -35,6 +35,9 @@
 #include <utils/threads.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
+#ifndef _WIN32
+#include <sys/file.h>
+#endif
 
 #include <assert.h>
 #include <dirent.h>
@@ -767,6 +770,12 @@
         return;
     }
 
+#ifndef _WIN32
+    if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) {
+        fclose(fin);
+        return;
+    }
+#endif
     char buf[1024];
     while (fgets(buf, sizeof(buf), fin)) {
         // format of each line:
@@ -795,8 +804,13 @@
             sharedRes->add(oass, oidmap, offset + 1, false);
             const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
             const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+            delete oidmap;
         }
     }
+
+#ifndef _WIN32
+    TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN));
+#endif
     fclose(fin);
 }
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index c91b27d..fd30c25 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -739,7 +739,7 @@
                         ALOGW("CREATING STRING CACHE OF %zu bytes",
                                 static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**)));
 #endif
-                        mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**));
+                        mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t*));
                         if (mCache == NULL) {
                             ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
                                     (int)(mHeader->stringCount*sizeof(char16_t**)));
@@ -3539,7 +3539,7 @@
             }
             if (pi != NULL) {
                 if (kDebugTableTheme) {
-                    ALOGI("Desired type index is %zd in avail %zu", t, Res_MAXTYPE + 1);
+                    ALOGI("Desired type index is %u in avail %zu", t, Res_MAXTYPE + 1);
                 }
                 if (t <= Res_MAXTYPE) {
                     const type_info& ti = pi->types[t];
@@ -4352,10 +4352,12 @@
             if (set->numAttrs >= set->availAttrs) {
                 // Need to alloc more memory...
                 const size_t newAvail = set->availAttrs+N;
+                void *oldSet = set;
                 set = (bag_set*)realloc(set,
                                         sizeof(bag_set)
                                         + sizeof(bag_entry)*newAvail);
                 if (set == NULL) {
+                    free(oldSet);
                     return NO_MEMORY;
                 }
                 set->availAttrs = newAvail;
@@ -4402,7 +4404,7 @@
         pos++;
         const size_t size = dtohs(map->value.size);
         curOff += size + sizeof(*map)-sizeof(map->value);
-    };
+    }
 
     if (curEntry > set->numAttrs) {
         set->numAttrs = curEntry;
diff --git a/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
similarity index 98%
rename from include/androidfw/Asset.h
rename to libs/androidfw/include/androidfw/Asset.h
index ee77e97..36efbe5 100644
--- a/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -48,7 +48,7 @@
 
     static int32_t getGlobalCount();
     static String8 getAssetAllocations();
-    
+
     /* used when opening an asset */
     typedef enum AccessMode {
         ACCESS_UNKNOWN = 0,
@@ -197,7 +197,7 @@
 
     AccessMode  mAccessMode;        // how the asset was opened
     String8    mAssetSource;       // debug string
-    
+
     Asset*		mNext;				// linked list.
     Asset*		mPrev;
 };
@@ -260,7 +260,7 @@
 
     FileMap*    mMap;           // for memory map
     unsigned char* mBuf;        // for read
-    
+
     const void* ensureAlignment(FileMap* map);
 };
 
@@ -297,7 +297,7 @@
     virtual const void* getBuffer(bool wordAligned);
     virtual off64_t getLength(void) const { return mUncompressedLen; }
     virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
-    virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const { return -1; }
+    virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
     virtual bool isAllocated(void) const { return mBuf != NULL; }
 
 private:
diff --git a/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
similarity index 100%
rename from include/androidfw/AssetDir.h
rename to libs/androidfw/include/androidfw/AssetDir.h
diff --git a/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
similarity index 97%
rename from include/androidfw/AssetManager.h
rename to libs/androidfw/include/androidfw/AssetManager.h
index 914ac3d..1a59e44 100644
--- a/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -33,17 +33,8 @@
 /*
  * Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
  */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 struct AAssetManager { };
 
-#ifdef __cplusplus
-};
-#endif
-
-
 /*
  * Now the proper C++ android-namespace definitions
  */
@@ -87,8 +78,8 @@
     virtual ~AssetManager(void);
 
     static int32_t getGlobalCount();
-    
-    /*                                                                       
+
+    /*
      * Add a new source for assets.  This can be called multiple times to
      * look in multiple places for assets.  It can be either a directory (for
      * finding assets as raw files on the disk) or a ZIP file.  This newly
@@ -203,7 +194,7 @@
      */
     FileType getFileType(const char* fileName);
 
-    /*                                                                       
+    /*
      * Return the complete resource table to find things in the package.
      */
     const ResTable& getResources(bool required = true) const;
@@ -302,12 +293,12 @@
 
         ResTable* getResourceTable();
         ResTable* setResourceTable(ResTable* res);
-        
+
         bool isUpToDate();
 
         void addOverlay(const asset_path& ap);
         bool getOverlay(size_t idx, asset_path* out) const;
-        
+
     protected:
         ~SharedZip();
 
@@ -359,7 +350,7 @@
 
         void addOverlay(const String8& path, const asset_path& overlay);
         bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
-        
+
     private:
         void closeZip(int idx);
 
diff --git a/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
similarity index 100%
rename from include/androidfw/AttributeFinder.h
rename to libs/androidfw/include/androidfw/AttributeFinder.h
diff --git a/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h
similarity index 100%
rename from include/androidfw/BackupHelpers.h
rename to libs/androidfw/include/androidfw/BackupHelpers.h
diff --git a/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
similarity index 100%
rename from include/androidfw/ByteBucketArray.h
rename to libs/androidfw/include/androidfw/ByteBucketArray.h
diff --git a/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
similarity index 100%
rename from include/androidfw/CursorWindow.h
rename to libs/androidfw/include/androidfw/CursorWindow.h
diff --git a/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
similarity index 100%
rename from include/androidfw/DisplayEventDispatcher.h
rename to libs/androidfw/include/androidfw/DisplayEventDispatcher.h
diff --git a/include/androidfw/LocaleData.h b/libs/androidfw/include/androidfw/LocaleData.h
similarity index 100%
rename from include/androidfw/LocaleData.h
rename to libs/androidfw/include/androidfw/LocaleData.h
diff --git a/include/androidfw/ObbFile.h b/libs/androidfw/include/androidfw/ObbFile.h
similarity index 96%
rename from include/androidfw/ObbFile.h
rename to libs/androidfw/include/androidfw/ObbFile.h
index 47559cd..3dbf997 100644
--- a/include/androidfw/ObbFile.h
+++ b/libs/androidfw/include/androidfw/ObbFile.h
@@ -124,20 +124,13 @@
     /* Flags for this OBB type. */
     int32_t mFlags;
 
-    /* Whether the file is salted. */
-    bool mSalted;
-
     /* The encryption salt. */
     unsigned char mSalt[8];
 
     const char* mFileName;
 
-    size_t mFileSize;
-
     size_t mFooterStart;
 
-    unsigned char* mReadBuf;
-
     bool parseObbFile(int fd);
 };
 
diff --git a/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
similarity index 100%
rename from include/androidfw/ResourceTypes.h
rename to libs/androidfw/include/androidfw/ResourceTypes.h
diff --git a/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h
similarity index 100%
rename from include/androidfw/StreamingZipInflater.h
rename to libs/androidfw/include/androidfw/StreamingZipInflater.h
diff --git a/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
similarity index 98%
rename from include/androidfw/TypeWrappers.h
rename to libs/androidfw/include/androidfw/TypeWrappers.h
index 7bdf8af..fd84873 100644
--- a/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -30,6 +30,7 @@
         iterator& operator=(const iterator& rhs) {
             mTypeVariant = rhs.mTypeVariant;
             mIndex = rhs.mIndex;
+            return *this;
         }
 
         bool operator==(const iterator& rhs) const {
diff --git a/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
similarity index 100%
rename from include/androidfw/ZipFileRO.h
rename to libs/androidfw/include/androidfw/ZipFileRO.h
diff --git a/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
similarity index 100%
rename from include/androidfw/ZipUtils.h
rename to libs/androidfw/include/androidfw/ZipUtils.h
diff --git a/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
similarity index 100%
rename from include/androidfw/misc.h
rename to libs/androidfw/include/androidfw/misc.h
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index c583988..0b46c09 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -22,6 +22,8 @@
 
 #include "Typeface.h"
 
+#include <pthread.h>
+
 #include "MinikinSkia.h"
 #include "SkTypeface.h"
 #include "SkPaint.h"
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index 2990952..dca78b3 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -24,7 +24,8 @@
     libprotobuf-cpp-lite \
     libharfbuzz_ng \
     libft2 \
-    libminikin
+    libminikin \
+    libandroidfw
 
 ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
     LOCAL_SHARED_LIBRARIES += libRS libRScpp
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3c1c0bc..0d9ede0 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -20,6 +20,7 @@
 #include "CanvasContext.h"
 #include "EglManager.h"
 #include "RenderProxy.h"
+#include "utils/FatVector.h"
 
 #include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
@@ -282,10 +283,18 @@
                 "RenderThread Looper POLL_ERROR!");
 
         nsecs_t nextWakeup;
-        // Process our queue, if we have anything
-        while (RenderTask* task = nextTask(&nextWakeup)) {
-            task->run();
-            // task may have deleted itself, do not reference it again
+        {
+            FatVector<RenderTask*, 10> workQueue;
+            // Process our queue, if we have anything. By first acquiring
+            // all the pending events then processing them we avoid vsync
+            // starvation if more tasks are queued while we are processing tasks.
+            while (RenderTask* task = nextTask(&nextWakeup)) {
+                workQueue.push_back(task);
+            }
+            for (auto task : workQueue) {
+                task->run();
+                // task may have deleted itself, do not reference it again
+            }
         }
         if (nextWakeup == LLONG_MAX) {
             timeoutMillis = -1;
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 84ef9c2..362890b 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -113,7 +113,7 @@
 
 void BM_FrameBuilder_defer_scene(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
-        const char* sceneName = *(SCENES.begin() + state.range_x());
+        const char* sceneName = *(SCENES.begin() + state.range(0));
         state.SetLabel(sceneName);
         auto node = getSyncedSceneNode(sceneName);
         while (state.KeepRunning()) {
@@ -129,7 +129,7 @@
 
 void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
-        const char* sceneName = *(SCENES.begin() + state.range_x());
+        const char* sceneName = *(SCENES.begin() + state.range(0));
         state.SetLabel(sceneName);
         auto node = getSyncedSceneNode(sceneName);
 
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index f90f1e2..fb91bbb 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -18,45 +18,69 @@
 
 import java.io.InputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import android.media.MediaCodec.BufferInfo;
+import android.util.Log;
 
 
 /**
  * AmrInputStream
  * @hide
  */
-public final class AmrInputStream extends InputStream
-{    
-    static {
-        System.loadLibrary("media_jni");
-    }
-    
+public final class AmrInputStream extends InputStream {
     private final static String TAG = "AmrInputStream";
     
     // frame is 20 msec at 8.000 khz
     private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
-    
+
+    MediaCodec mCodec;
+    BufferInfo mInfo;
+    boolean mSawOutputEOS;
+    boolean mSawInputEOS;
+
     // pcm input stream
     private InputStream mInputStream;
-    
-    // native handle
-    private long mGae;
-    
+
     // result amr stream
     private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
     private int mBufIn = 0;
     private int mBufOut = 0;
-    
+
     // helper for bytewise read()
     private byte[] mOneByte = new byte[1];
-    
+
     /**
      * Create a new AmrInputStream, which converts 16 bit PCM to AMR
      * @param inputStream InputStream containing 16 bit PCM.
      */
     public AmrInputStream(InputStream inputStream) {
         mInputStream = inputStream;
-        mGae = GsmAmrEncoderNew();
-        GsmAmrEncoderInitialize(mGae);
+
+        MediaFormat format  = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+        format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
+        format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, 12200);
+
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        String name = mcl.findEncoderForFormat(format);
+        if (name != null) {
+            try {
+                mCodec = MediaCodec.createByCodecName(name);
+                mCodec.configure(format,
+                        null /* surface */,
+                        null /* crypto */,
+                        MediaCodec.CONFIGURE_FLAG_ENCODE);
+                mCodec.start();
+            } catch (IOException e) {
+                if (mCodec != null) {
+                    mCodec.release();
+                }
+                mCodec = null;
+            }
+        }
+        mInfo = new BufferInfo();
     }
 
     @Override
@@ -64,7 +88,7 @@
         int rtn = read(mOneByte, 0, 1);
         return rtn == 1 ? (0xff & mOneByte[0]) : -1;
     }
-    
+
     @Override
     public int read(byte[] b) throws IOException {
         return read(b, 0, b.length);
@@ -72,67 +96,100 @@
 
     @Override
     public int read(byte[] b, int offset, int length) throws IOException {
-        if (mGae == 0) throw new IllegalStateException("not open");
-        
-        // local buffer of amr encoded audio empty
-        if (mBufOut >= mBufIn) {
-            // reset the buffer
+        if (mCodec == null) {
+            throw new IllegalStateException("not open");
+        }
+
+        if (mBufOut >= mBufIn && !mSawOutputEOS) {
+            // no data left in buffer, refill it
             mBufOut = 0;
             mBufIn = 0;
-            
-            // fetch a 20 msec frame of pcm
-            for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) {
-                int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i);
-                if (n == -1) return -1;
-                i += n;
+
+            // first push as much data into the encoder as possible
+            while (!mSawInputEOS) {
+                int index = mCodec.dequeueInputBuffer(0);
+                if (index < 0) {
+                    // no input buffer currently available
+                    break;
+                } else {
+                    int numRead;
+                    for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) {
+                        int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead);
+                        if (n == -1) {
+                            mSawInputEOS = true;
+                            break;
+                        }
+                        numRead += n;
+                    }
+                    ByteBuffer buf = mCodec.getInputBuffer(index);
+                    buf.put(mBuf, 0, numRead);
+                    mCodec.queueInputBuffer(index,
+                            0 /* offset */,
+                            numRead,
+                            0 /* presentationTimeUs */,
+                            mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */);
+                }
             }
-            
-            // encode it
-            mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0);
+
+            // now read encoded data from the encoder (blocking, since we just filled up the
+            // encoder's input with data it should be able to output at least one buffer)
+            while (true) {
+                int index = mCodec.dequeueOutputBuffer(mInfo, -1);
+                if (index >= 0) {
+                    mBufIn = mInfo.size;
+                    ByteBuffer out = mCodec.getOutputBuffer(index);
+                    out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+                    mCodec.releaseOutputBuffer(index,  false /* render */);
+                    if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        mSawOutputEOS = true;
+                    }
+                    break;
+                }
+            }
         }
-        
-        // return encoded audio to user
-        if (length > mBufIn - mBufOut) length = mBufIn - mBufOut;
-        System.arraycopy(mBuf, mBufOut, b, offset, length);
-        mBufOut += length;
-        
-        return length;
+
+        if (mBufOut < mBufIn) {
+            // there is data in the buffer
+            if (length > mBufIn - mBufOut) {
+                length = mBufIn - mBufOut;
+            }
+            System.arraycopy(mBuf, mBufOut, b, offset, length);
+            mBufOut += length;
+            return length;
+        }
+
+        if (mSawInputEOS && mSawOutputEOS) {
+            // no more data available in buffer, codec or input stream
+            return -1;
+        }
+
+        // caller should try again
+        return 0;
     }
 
     @Override
     public void close() throws IOException {
         try {
-            if (mInputStream != null) mInputStream.close();
+            if (mInputStream != null) {
+                mInputStream.close();
+            }
         } finally {
             mInputStream = null;
             try {
-                if (mGae != 0) GsmAmrEncoderCleanup(mGae);
-            } finally {
-                try {
-                    if (mGae != 0) GsmAmrEncoderDelete(mGae);
-                } finally {
-                    mGae = 0;
+                if (mCodec != null) {
+                    mCodec.release();
                 }
+            } finally {
+                mCodec = null;
             }
         }
     }
 
     @Override
     protected void finalize() throws Throwable {
-        if (mGae != 0) {
-            close();
-            throw new IllegalStateException("someone forgot to close AmrInputStream");
+        if (mCodec != null) {
+            Log.w(TAG, "AmrInputStream wasn't closed");
+            mCodec.release();
         }
     }
-    
-    //
-    // AudioRecord JNI interface
-    //
-    private static native long GsmAmrEncoderNew();
-    private static native void GsmAmrEncoderInitialize(long gae);
-    private static native int GsmAmrEncoderEncode(long gae,
-            byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException;
-    private static native void GsmAmrEncoderCleanup(long gae);
-    private static native void GsmAmrEncoderDelete(long gae);
-
 }
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 2c28a10..8640565 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    android_media_AmrInputStream.cpp \
     android_media_ExifInterface.cpp \
     android_media_ImageWriter.cpp \
     android_media_ImageReader.cpp \
@@ -46,10 +45,9 @@
     libusbhost \
     libexif \
     libpiex \
-    libstagefright_amrnb_common
+    libandroidfw
 
 LOCAL_STATIC_LIBRARIES := \
-    libstagefright_amrnbenc
 
 LOCAL_C_INCLUDES += \
     external/libexif/ \
@@ -59,9 +57,6 @@
     frameworks/base/libs/hwui \
     frameworks/av/media/libmedia \
     frameworks/av/media/libstagefright \
-    frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
-    frameworks/av/media/libstagefright/codecs/amrnb/common \
-    frameworks/av/media/libstagefright/codecs/amrnb/common/include \
     frameworks/av/media/mtp \
     frameworks/native/include/media/openmax \
     $(call include-path-for, libhardware)/hardware \
diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp
deleted file mode 100644
index b56a364..0000000
--- a/media/jni/android_media_AmrInputStream.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
-**
-** Copyright 2007, 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.
-*/
-
-#define LOG_TAG "AmrInputStream"
-#include "utils/Log.h"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "gsmamr_enc.h"
-
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-// Corresponds to max bit rate of 12.2 kbps.
-static const int MAX_OUTPUT_BUFFER_SIZE = 32;
-static const int FRAME_DURATION_MS = 20;
-static const int SAMPLING_RATE_HZ = 8000;
-static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000);
-static const int BYTES_PER_SAMPLE = 2;  // Assume 16-bit PCM samples
-static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE);
-
-struct GsmAmrEncoderState {
-    GsmAmrEncoderState()
-        : mEncState(NULL),
-          mSidState(NULL),
-          mLastModeUsed(0) {
-    }
-
-    ~GsmAmrEncoderState() {}
-
-    void*   mEncState;
-    void*   mSidState;
-    int32_t mLastModeUsed;
-};
-
-static jlong android_media_AmrInputStream_GsmAmrEncoderNew
-        (JNIEnv *env, jclass /* clazz */) {
-    GsmAmrEncoderState* gae = new GsmAmrEncoderState();
-    if (gae == NULL) {
-        jniThrowRuntimeException(env, "Out of memory");
-    }
-    return (jlong)gae;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderInitialize
-        (JNIEnv *env, jclass /* clazz */, jlong gae) {
-    GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
-    int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false);
-    if (nResult != OK) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "GsmAmrEncoder initialization failed %d", nResult);
-    }
-}
-
-static jint android_media_AmrInputStream_GsmAmrEncoderEncode
-        (JNIEnv *env, jclass /* clazz */,
-         jlong gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) {
-
-    jbyte inBuf[BYTES_PER_FRAME];
-    jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE];
-
-    env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf);
-    GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
-    int32_t length = AMREncode(state->mEncState, state->mSidState,
-                                (Mode) MR122,
-                                (int16_t *) inBuf,
-                                (unsigned char *) outBuf,
-                                (Frame_Type_3GPP*) &state->mLastModeUsed,
-                                AMR_TX_WMF);
-    if (length < 0) {
-        jniThrowExceptionFmt(env, "java/io/IOException",
-                "Failed to encode a frame with error code: %d", length);
-        return (jint)-1;
-    }
-
-    // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum)
-    // bitpacked, i.e.;
-    //    [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0
-    // Here we are converting the header to be as specified in Section 5.3 of
-    // RFC 3267 (AMR storage format) i.e.
-    //    [P(1) + FT(4) + Q(1) + P(2)].
-    if (length > 0) {
-      outBuf[0] = (outBuf[0] << 3) | 0x4;
-    }
-
-    env->SetByteArrayRegion(amr, amrOffset, length, outBuf);
-
-    return (jint)length;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderCleanup
-        (JNIEnv* /* env */, jclass /* clazz */, jlong gae) {
-    GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
-    AMREncodeExit(&state->mEncState, &state->mSidState);
-    state->mEncState = NULL;
-    state->mSidState = NULL;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderDelete
-        (JNIEnv* /* env */, jclass /* clazz */, jlong gae) {
-    delete (GsmAmrEncoderState*)gae;
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gMethods[] = {
-    {"GsmAmrEncoderNew",        "()J",        (void*)android_media_AmrInputStream_GsmAmrEncoderNew},
-    {"GsmAmrEncoderInitialize", "(J)V",       (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize},
-    {"GsmAmrEncoderEncode",     "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode},
-    {"GsmAmrEncoderCleanup",    "(J)V",       (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup},
-    {"GsmAmrEncoderDelete",     "(J)V",       (void*)android_media_AmrInputStream_GsmAmrEncoderDelete},
-};
-
-
-int register_android_media_AmrInputStream(JNIEnv *env)
-{
-    const char* const kClassPathName = "android/media/AmrInputStream";
-
-    return AndroidRuntime::registerNativeMethods(env,
-            kClassPathName, gMethods, NELEM(gMethods));
-}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2fb1a3b..8f14b79 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1106,7 +1106,6 @@
 extern int register_android_media_MediaSync(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
-extern int register_android_media_AmrInputStream(JNIEnv *env);
 extern int register_android_mtp_MtpDatabase(JNIEnv *env);
 extern int register_android_mtp_MtpDevice(JNIEnv *env);
 extern int register_android_mtp_MtpServer(JNIEnv *env);
@@ -1152,11 +1151,6 @@
         goto bail;
     }
 
-    if (register_android_media_AmrInputStream(env) < 0) {
-        ALOGE("ERROR: AmrInputStream native registration failed\n");
-        goto bail;
-    }
-
     if (register_android_media_ResampleInputStream(env) < 0) {
         ALOGE("ERROR: ResampleInputStream native registration failed\n");
         goto bail;
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index 5c22c9b..8bd8857 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -11,7 +11,8 @@
     libutils \
     libandroid_runtime \
     libnativehelper \
-    libmedia
+    libmedia \
+    libaudioclient \
 
 LOCAL_C_INCLUDES := \
     $(call include-path-for, audio-effects)
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 2bc41b5..509a59b 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -12,7 +12,7 @@
     libutils \
     libandroid_runtime \
     libnativehelper \
-    libmedia \
+    libaudioclient \
     libmediandk \
     libbinder
 
diff --git a/media/tests/audiotests/Android.mk b/media/tests/audiotests/Android.mk
index 57be372..01e42bd 100644
--- a/media/tests/audiotests/Android.mk
+++ b/media/tests/audiotests/Android.mk
@@ -15,7 +15,8 @@
     libutils \
     libbinder \
     libhardware_legacy \
-    libmedia
+    libmedia \
+    libaudioclient \
 
 LOCAL_MODULE_TAGS := tests
 
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 175f730..4c8a9db 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
-    libskia
+    libskia \
+    libandroidfw
 
 LOCAL_C_INCLUDES += \
     frameworks/base/native/include \
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 7a96430..9018d0e 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -23,6 +23,7 @@
 
 #include <linux/fuse.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
 
 #include <map>
 
diff --git a/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
index 518e64e..63b1903 100644
--- a/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
+++ b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
@@ -134,8 +134,8 @@
     public HomeSP addSP(MOTree instanceTree) throws IOException, SAXException {
         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         String xml = instanceTree.toXml();
-        wifiManager.addPasspointManagementObject(xml);
-        return MOManager.buildSP(xml);
+        // TODO(b/32883320): use the new API for adding Passpoint configuration.
+        return null;
     }
 
     public void removeSP(String fqdn) throws IOException {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 9608daa..24ede16 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -116,6 +116,10 @@
         List<BluetoothDevice> sinks = getConnectedDevices();
         if (sinks != null) {
             for (BluetoothDevice sink : sinks) {
+                if (sink.equals(device)) {
+                    Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+                    continue;
+                }
                 mService.disconnect(sink);
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 4bcbea7..a332332 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -268,16 +268,16 @@
             if (cachedDevice == null) {
                 Log.w(TAG, "CachedBluetoothDevice for device " + device +
                         " not found, calling readPairedDevices().");
-                if (!readPairedDevices()) {
-                    Log.e(TAG, "Got bonding state changed for " + device +
-                            ", but we have no record of that device.");
-                    return;
+                if (readPairedDevices()) {
+                    cachedDevice = mDeviceManager.findDevice(device);
                 }
-                cachedDevice = mDeviceManager.findDevice(device);
+
                 if (cachedDevice == null) {
-                    Log.e(TAG, "Got bonding state changed for " + device +
-                            ", but device not added in cache.");
-                    return;
+                    Log.w(TAG, "Got bonding state changed for " + device +
+                            ", but we have no record of that device.");
+
+                    cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+                    dispatchDeviceAdded(cachedDevice);
                 }
             }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7f0e27a..1ea592d 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothMapClient;
 import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothPbapClient;
@@ -81,12 +82,14 @@
     private HeadsetProfile mHeadsetProfile;
     private HfpClientProfile mHfpClientProfile;
     private MapProfile mMapProfile;
+    private MapClientProfile mMapClientProfile;
     private final HidProfile mHidProfile;
     private OppProfile mOppProfile;
     private final PanProfile mPanProfile;
     private PbapClientProfile mPbapClientProfile;
     private final PbapServerProfile mPbapProfile;
     private final boolean mUsePbapPce;
+    private final boolean mUseMapClient;
 
     /**
      * Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -104,6 +107,8 @@
         mDeviceManager = deviceManager;
         mEventManager = eventManager;
         mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
+        // MAP Client is typically used in the same situations as PBAP Client
+        mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
         // pass this reference to adapter and event manager (circular dependency)
         mLocalAdapter.setProfileManager(this);
         mEventManager.setProfileManager(this);
@@ -125,10 +130,15 @@
                 BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
 
         if(DEBUG) Log.d(TAG, "Adding local MAP profile");
-        mMapProfile = new MapProfile(mContext, mLocalAdapter,
-                mDeviceManager, this);
-        addProfile(mMapProfile, MapProfile.NAME,
-                BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+        if (mUseMapClient) {
+            mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mMapClientProfile, MapClientProfile.NAME,
+                BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+        } else {
+            mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mMapProfile, MapProfile.NAME,
+                    BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+        }
 
        //Create PBAP server profile, but do not add it to list of profiles
        // as we do not need to monitor the profile as part of profile list
@@ -199,6 +209,22 @@
             Log.d(TAG, "Handsfree Uuid not found.");
         }
 
+        // Message Access Profile Client
+        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
+            if (mMapClientProfile == null) {
+                if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
+                mMapClientProfile =
+                        new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                addProfile(mMapClientProfile, MapClientProfile.NAME,
+                        BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+            }
+        } else if (mMapClientProfile != null) {
+            Log.w(TAG,
+                    "Warning: MAP Client profile was previously added but the UUID is now missing.");
+        } else {
+            Log.d(TAG, "MAP Client Uuid not found.");
+        }
+
         // OPP
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
             if (mOppProfile == null) {
@@ -383,6 +409,10 @@
         return mMapProfile;
     }
 
+    public MapClientProfile getMapClientProfile() {
+        return mMapClientProfile;
+    }
+
     /**
      * Fill in a list of LocalBluetoothProfile objects that are supported by
      * the local device and the remote device.
@@ -465,6 +495,11 @@
             mMapProfile.setPreferred(device, true);
         }
 
+        if (mMapClientProfile != null) {
+            profiles.add(mMapClientProfile);
+            removedProfiles.remove(mMapClientProfile);
+        }
+
         if (mUsePbapPce) {
             profiles.add(mPbapClientProfile);
             removedProfiles.remove(mPbapClientProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
new file mode 100644
index 0000000..a7621fc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MapClientProfile handles Bluetooth MAP profile.
+ */
+public final class MapClientProfile implements LocalBluetoothProfile {
+    private static final String TAG = "MapClientProfile";
+    private static boolean V = false;
+
+    private BluetoothMapClient mService;
+    private boolean mIsProfileReady;
+
+    private final LocalBluetoothAdapter mLocalAdapter;
+    private final CachedBluetoothDeviceManager mDeviceManager;
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    static final ParcelUuid[] UUIDS = {
+        BluetoothUuid.MAP,
+        BluetoothUuid.MNS,
+        BluetoothUuid.MAS,
+    };
+
+    static final String NAME = "MAP Client";
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 0;
+
+    // These callbacks run on the main thread.
+    private final class MapClientServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (V) Log.d(TAG,"Bluetooth service connected");
+            mService = (BluetoothMapClient) proxy;
+            // We just bound to the service, so refresh the UI for any connected MAP devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            while (!deviceList.isEmpty()) {
+                BluetoothDevice nextDevice = deviceList.remove(0);
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    Log.w(TAG, "MapProfile found new device: " + nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                }
+                device.onProfileStateChanged(MapClientProfile.this,
+                        BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+
+            mProfileManager.callServiceConnectedListeners();
+            mIsProfileReady=true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            mProfileManager.callServiceDisconnectedListeners();
+            mIsProfileReady=false;
+        }
+    }
+
+    public boolean isProfileReady() {
+        if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+        return mIsProfileReady;
+    }
+
+    MapClientProfile(Context context, LocalBluetoothAdapter adapter,
+            CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mLocalAdapter = adapter;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        mLocalAdapter.getProfileProxy(context, new MapClientServiceListener(),
+                BluetoothProfile.MAP_CLIENT);
+    }
+
+    public boolean isConnectable() {
+        return true;
+    }
+
+    public boolean isAutoConnectable() {
+        return true;
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        if (mService == null) return false;
+        List<BluetoothDevice> connectedDevices = getConnectedDevices();
+        if (connectedDevices != null) {
+            for (BluetoothDevice connectedDevice : connectedDevices) {
+                mService.disconnect(connectedDevice);
+            }
+        }
+        return mService.connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        if (mService == null) return false;
+        // Downgrade priority as user is disconnecting.
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+
+        return mService.getConnectionState(device);
+    }
+
+    public boolean isPreferred(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+    }
+
+    public int getPreferred(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        return mService.getPriority(device);
+    }
+
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        if (mService == null) return;
+        if (preferred) {
+            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            }
+        } else {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+        }
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        return mService.getDevicesMatchingConnectionStates(
+              new int[] {BluetoothProfile.STATE_CONNECTED,
+                         BluetoothProfile.STATE_CONNECTING,
+                         BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_map;
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_map_profile_summary_use_for;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_map_profile_summary_connected;
+
+            default:
+                return Utils.getConnectionStateSummary(state);
+        }
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return R.drawable.ic_bt_cellphone;
+    }
+
+    protected void finalize() {
+        if (V) Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
+                                                                       mService);
+                mService = null;
+            }catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up MAP Client proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index d7c9eab..72a3b3a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -145,7 +145,7 @@
                 }
             }
             for (BluetoothDevice src : srcs) {
-                mService.disconnect(device);
+                mService.disconnect(src);
             }
         }
         Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 418b138..994f038 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -123,9 +123,9 @@
                 // Only add Settings for this user.
                 getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
                 getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
-                        OPERATOR_DEFAULT_CATEGORY, tiles, false);
+                        OPERATOR_DEFAULT_CATEGORY, tiles, false, true);
                 getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
-                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false);
+                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true);
             }
             getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
         }
@@ -177,12 +177,20 @@
     private static void getTilesForAction(Context context,
             UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
             String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
+        getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
+                requireSettings, requireSettings);
+    }
+
+    private static void getTilesForAction(Context context,
+            UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
+            String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
+            boolean usePriority) {
         Intent intent = new Intent(action);
         if (requireSettings) {
             intent.setPackage(SETTING_PKG);
         }
         getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
-                requireSettings, true);
+                usePriority, true);
     }
 
     public static void getTilesForIntent(Context context, UserHandle user, Intent intent,
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 0a3f0c0..63d1b1e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -254,7 +254,7 @@
 
     public boolean matches(WifiConfiguration config) {
         if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
-            return config.FQDN.equals(mConfig.providerFriendlyName);
+            return config.FQDN.equals(mConfig.FQDN);
         } else {
             return ssid.equals(removeDoubleQuotes(config.SSID))
                     && security == getSecurity(config)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 636a9a50..9d4bb48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -215,7 +215,7 @@
     }
 
     public void onCollapse() {
-        if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+        if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
         }
     }
@@ -392,7 +392,7 @@
     }
 
     public void closeDetail() {
-        if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+        if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             // Treat this as a detail panel for now, to make things easy.
             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 0de1e30..77da6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -162,6 +162,10 @@
         }
     }
 
+    public boolean isShown() {
+        return isShown;
+    }
+
     private void setCustomizing(boolean customizing) {
         mCustomizing = customizing;
         mQsContainer.notifyCustomizeChanged();
@@ -216,7 +220,9 @@
     private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            setCustomizing(true);
+            if (isShown) {
+                setCustomizing(true);
+            }
             mNotifQsContainer.setCustomizerAnimating(false);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 1431b22..063c59d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -65,7 +65,10 @@
         for (int i = 0; i < possibleTiles.length; i++) {
             final String spec = possibleTiles[i];
             final QSTile<?> tile = host.createTile(spec);
-            if (tile == null || !tile.isAvailable()) {
+            if (tile == null) {
+                continue;
+            } else if (!tile.isAvailable()) {
+                tile.destroy();
                 continue;
             }
             tile.setListening(this, true);
@@ -79,6 +82,7 @@
                     tile.getState().copyTo(state);
                     // Ignore the current state and get the generic label instead.
                     state.label = tile.getTileLabel();
+                    tile.destroy();
                     mainHandler.post(new Runnable() {
                         @Override
                         public void run() {
@@ -127,6 +131,7 @@
         state.label = label;
         state.contentDescription = label;
         state.icon = new DrawableIcon(drawable);
+        state.autoMirrorDrawable = false;
         addTile(spec, appLabel, state, false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d3f5d26..0d49bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -214,7 +214,9 @@
 
     @Override
     public State newTileState() {
-        return new State();
+        State state = new State();
+        state.autoMirrorDrawable = false;
+        return state;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 04cb553..4a3fea8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -68,12 +68,23 @@
 
     private boolean mListening;
     private boolean mShowingDetail;
+    private boolean mReceiverRegistered;
 
     public DndTile(Host host) {
         super(host);
         mController = host.getZenModeController();
         mDetailAdapter = new DndDetailAdapter();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
+        mReceiverRegistered = true;
+    }
+
+    @Override
+    protected void handleDestroy() {
+        super.handleDestroy();
+        if (mReceiverRegistered) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiverRegistered = false;
+        }
     }
 
     public static void setVisible(Context context, boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index dd80750..005206f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -22,39 +22,93 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 
-public abstract class CurrentUserTracker extends BroadcastReceiver {
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
 
-    private Context mContext;
-    private int mCurrentUserId;
+public abstract class CurrentUserTracker {
+    private final UserReceiver mUserReceiver;
+
+    private Consumer<Integer> mCallback = this::onUserSwitched;
 
     public CurrentUserTracker(Context context) {
-        mContext = context;
+        mUserReceiver = UserReceiver.getInstance(context);
     }
 
     public int getCurrentUserId() {
-        return mCurrentUserId;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
-            int oldUserId = mCurrentUserId;
-            mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-            if (oldUserId != mCurrentUserId) {
-                onUserSwitched(mCurrentUserId);
-            }
-        }
+        return mUserReceiver.getCurrentUserId();
     }
 
     public void startTracking() {
-        mCurrentUserId = ActivityManager.getCurrentUser();
-        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(this, filter);
+        mUserReceiver.addTracker(mCallback);
     }
 
     public void stopTracking() {
-        mContext.unregisterReceiver(this);
+        mUserReceiver.removeTracker(mCallback);
     }
 
     public abstract void onUserSwitched(int newUserId);
+
+    private static class UserReceiver extends BroadcastReceiver {
+        private static UserReceiver sInstance;
+
+        private Context mAppContext;
+        private boolean mReceiverRegistered;
+        private int mCurrentUserId;
+
+        private List<Consumer<Integer>> mCallbacks = new ArrayList<>();
+
+        private UserReceiver(Context context) {
+            mAppContext = context.getApplicationContext();
+        }
+
+        static UserReceiver getInstance(Context context) {
+            if (sInstance == null) {
+                sInstance = new UserReceiver(context);
+            }
+            return sInstance;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+                notifyUserSwitched(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+            }
+        }
+
+        public int getCurrentUserId() {
+            return mCurrentUserId;
+        }
+
+        private void addTracker(Consumer<Integer> callback) {
+            if (!mCallbacks.contains(callback)) {
+                mCallbacks.add(callback);
+            }
+            if (!mReceiverRegistered) {
+                mCurrentUserId = ActivityManager.getCurrentUser();
+                IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+                mAppContext.registerReceiver(this, filter);
+                mReceiverRegistered = true;
+            }
+        }
+
+        private void removeTracker(Consumer<Integer> callback) {
+            if (mCallbacks.contains(callback)) {
+                mCallbacks.remove(callback);
+                if (mCallbacks.size() == 0 && mReceiverRegistered) {
+                    mAppContext.unregisterReceiver(this);
+                    mReceiverRegistered = false;
+                }
+            }
+        }
+
+        private void notifyUserSwitched(int newUserId) {
+            if (mCurrentUserId != newUserId) {
+                mCurrentUserId = newUserId;
+                for (Consumer<Integer> consumer : mCallbacks) {
+                    consumer.accept(newUserId);
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 998f50f..49b51e4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1048,6 +1048,7 @@
 
     public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) {
         if (mGrowRecents && getWindowManagerProxy().getDockSide() == WindowManager.DOCKED_TOP
+                && getSnapAlgorithm().getMiddleTarget() != getSnapAlgorithm().getLastSplitTarget()
                 && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) {
             mState.growAfterRecentsDrawn = true;
             startDragging(false /* animate */, false /* touching */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ebfa018..fa14346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -159,6 +159,14 @@
         mBrightnessMirror = findViewById(R.id.brightness_mirror);
     }
 
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        if (child.getId() == R.id.brightness_mirror) {
+            mBrightnessMirror = child;
+        }
+    }
+
     public void setService(PhoneStatusBar service) {
         mService = service;
         mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 70f2fdc..7afdbcb 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -104,7 +104,9 @@
                 TunerService.showResetRequest(getContext(), new Runnable() {
                     @Override
                     public void run() {
-                        getActivity().finish();
+                        if (getActivity() != null) {
+                            getActivity().finish();
+                        }
                     }
                 });
                 return true;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 6575a2a..5b36598 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -35,10 +35,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -56,6 +58,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -115,7 +118,6 @@
     private static final int SERVICE_IBLUETOOTHGATT = 2;
 
     private final Context mContext;
-    private static int mBleAppCount = 0;
 
     // Locks are not provided for mName and mAddress.
     // They are accessed in handler or broadcast receiver, same thread context.
@@ -151,6 +153,8 @@
     private final Map <Integer, ProfileServiceConnections> mProfileServices =
             new HashMap <Integer, ProfileServiceConnections>();
 
+    private final boolean mPermissionReviewRequired;
+
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
         final String airplaneModeRadios = Settings.Global.getString(resolver,
@@ -207,16 +211,14 @@
 
                     if (isAirplaneModeOn()) {
                         // Clear registered LE apps to force shut-off
-                        synchronized (this) {
-                            mBleAppCount = 0;
-                            mBleApps.clear();
-                        }
+                        clearBleApps();
                         if (st == BluetoothAdapter.STATE_BLE_ON) {
                             //if state is BLE_ON make sure you trigger disableBLE part
                             try {
                                 mBluetoothLock.readLock().lock();
                                 if (mBluetooth != null) {
                                     mBluetooth.onBrEdrDown();
+                                    mEnable = false;
                                     mEnableExternal = false;
                                 }
                             } catch (RemoteException e) {
@@ -243,6 +245,11 @@
         mHandler = new BluetoothHandler(IoThread.get().getLooper());
 
         mContext = context;
+
+        mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED
+                    || context.getResources().getBoolean(
+                com.android.internal.R.bool.config_permissionReviewRequired);
+
         mBluetooth = null;
         mBluetoothBinder = null;
         mBluetoothGatt = null;
@@ -427,30 +434,35 @@
 
     class ClientDeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
-            if (DBG) Slog.d(TAG, "Binder is dead -  unregister Ble App");
-            if (mBleAppCount > 0) --mBleAppCount;
-
-            if (mBleAppCount == 0) {
-                if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
-                try {
-                    mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) {
-                        mBluetooth.onBrEdrDown();
-                    }
-                } catch (RemoteException e) {
-                     Slog.e(TAG,"Unable to call onBrEdrDown", e);
-                } finally {
-                    mBluetoothLock.readLock().unlock();
+            if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
+            if (isBleAppPresent()) {
+              // Nothing to do, another app is here.
+              return;
+            }
+            if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
+            try {
+                mBluetoothLock.readLock().lock();
+                if (mBluetooth != null &&
+                    mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                    mEnable = false;
+                    mBluetooth.onBrEdrDown();
                 }
+            } catch (RemoteException e) {
+                 Slog.e(TAG,"Unable to call onBrEdrDown", e);
+            } finally {
+                mBluetoothLock.readLock().unlock();
             }
         }
     }
 
     /** Internal death rec list */
-    Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
+    Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
 
     @Override
     public boolean isBleScanAlwaysAvailable() {
+        if (isAirplaneModeOn() && !mEnable) {
+            return false;
+        }
         try {
             return (Settings.Global.getInt(mContentResolver,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0;
@@ -464,17 +476,20 @@
         ContentObserver contentObserver = new ContentObserver(null) {
             @Override
             public void onChange(boolean selfChange) {
-                if (!isBleScanAlwaysAvailable()) {
-                    disableBleScanMode();
-                    clearBleApps();
-                    try {
-                        mBluetoothLock.readLock().lock();
-                        if (mBluetooth != null) mBluetooth.onBrEdrDown();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error when disabling bluetooth", e);
-                    } finally {
-                        mBluetoothLock.readLock().unlock();
-                    }
+                if (isBleScanAlwaysAvailable()) {
+                  // Nothing to do
+                  return;
+                }
+                // BLE scan is not available.
+                disableBleScanMode();
+                clearBleApps();
+                try {
+                    mBluetoothLock.readLock().lock();
+                    if (mBluetooth != null) mBluetooth.onBrEdrDown();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error when disabling bluetooth", e);
+                } finally {
+                    mBluetoothLock.readLock().unlock();
                 }
             }
         };
@@ -510,9 +525,6 @@
                     throw new IllegalArgumentException("Wake lock is already dead.");
                 }
                 mBleApps.put(token, deathRec);
-                synchronized (this) {
-                    ++mBleAppCount;
-                }
                 if (DBG) Slog.d(TAG, "Registered for death Notification");
             }
 
@@ -522,31 +534,26 @@
                 // Unregister death recipient as the app goes away.
                 token.unlinkToDeath(r, 0);
                 mBleApps.remove(token);
-                synchronized (this) {
-                    if (mBleAppCount > 0) --mBleAppCount;
-                }
                 if (DBG) Slog.d(TAG, "Unregistered for death Notification");
             }
         }
-        if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount);
-        if (mBleAppCount == 0 && mEnable) {
+        int appCount = mBleApps.size();
+        if (DBG) Slog.d(TAG, appCount + " registered Ble Apps");
+        if (appCount == 0 && mEnable) {
             disableBleScanMode();
         }
-        return mBleAppCount;
+        return appCount;
     }
 
     // Clear all apps using BLE scan only mode.
     private void clearBleApps() {
-        synchronized (this) {
-            mBleApps.clear();
-            mBleAppCount = 0;
-        }
+        mBleApps.clear();
     }
 
     /** @hide*/
     public boolean isBleAppPresent() {
-        if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
-        return (mBleAppCount > 0);
+        if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+        return mBleApps.size() > 0;
     }
 
     /**
@@ -626,15 +633,26 @@
         return true;
     }
 
-    public boolean enable() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"enable(): not allowed for non-active and non system user");
-            return false;
+    public boolean enable(String packageName) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
+
+        if (!callerSystem) {
+            if (!checkIfCallerIsForegroundUser()) {
+                Slog.w(TAG, "enable(): not allowed for non-active and non system user");
+                return false;
+            }
+
+            mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                    "Need BLUETOOTH ADMIN permission");
+
+            if (!isEnabled() && mPermissionReviewRequired
+                    && startConsentUiIfNeeded(packageName, callingUid,
+                            BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
+                return false;
+            }
         }
 
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Slog.d(TAG,"enable():  mBluetooth =" + mBluetooth +
                     " mBinding = " + mBinding + " mState = " + mState);
@@ -650,14 +668,24 @@
         return true;
     }
 
-    public boolean disable(boolean persist) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
+    public boolean disable(String packageName, boolean persist) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
 
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"disable(): not allowed for non-active and non system user");
-            return false;
+        if (!callerSystem) {
+            if (!checkIfCallerIsForegroundUser()) {
+                Slog.w(TAG, "disable(): not allowed for non-active and non system user");
+                return false;
+            }
+
+            mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                    "Need BLUETOOTH ADMIN permission");
+
+            if (isEnabled() && mPermissionReviewRequired
+                    && startConsentUiIfNeeded(packageName, callingUid,
+                            BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
+                return false;
+            }
         }
 
         if (DBG) {
@@ -678,6 +706,31 @@
         return true;
     }
 
+    private boolean startConsentUiIfNeeded(String packageName,
+            int callingUid, String intentAction) throws RemoteException {
+        try {
+            // Validate the package only if we are going to use it
+            ApplicationInfo applicationInfo = mContext.getPackageManager()
+                    .getApplicationInfoAsUser(packageName,
+                            PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.getUserId(callingUid));
+            if (applicationInfo.uid != callingUid) {
+                throw new SecurityException("Package " + callingUid
+                        + " not in uid " + callingUid);
+            }
+
+            // Legacy apps in permission review mode trigger a user prompt
+            if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                Intent intent = new Intent(intentAction);
+                mContext.startActivity(intent);
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RemoteException(e.getMessage());
+        }
+        return false;
+    }
+
     public void unbindAndFinish() {
         if (DBG) {
             Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
@@ -1356,12 +1409,12 @@
                     if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
                             (newState == BluetoothAdapter.STATE_OFF) &&
                             (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError();
+                        recoverBluetoothServiceFromError(false);
                     }
                     if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
                             (newState == BluetoothAdapter.STATE_BLE_ON) &&
                             (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError();
+                        recoverBluetoothServiceFromError(true);
                     }
                     // If we tried to enable BT while BT was in the process of shutting down,
                     // wait for the BT process to fully tear down and then force a restart
@@ -1776,7 +1829,7 @@
                              quietMode ? 1 : 0, 0));
     }
 
-    private void recoverBluetoothServiceFromError() {
+    private void recoverBluetoothServiceFromError(boolean clearBle) {
         Slog.e(TAG,"recoverBluetoothServiceFromError");
         try {
             mBluetoothLock.readLock().lock();
@@ -1814,6 +1867,10 @@
         mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
         mState = BluetoothAdapter.STATE_OFF;
 
+        if (clearBle) {
+          clearBleApps();
+        }
+
         mEnable = false;
 
         if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f16376d..97b0981 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -128,6 +128,7 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.KeepaliveTracker;
+import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
@@ -760,7 +761,8 @@
         mTestMode = SystemProperties.get("cm.test.mode").equals("true")
                 && SystemProperties.get("ro.build.type").equals("eng");
 
-        mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager);
+        mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager,
+                                   IoThread.get().getLooper(), new MockableSystemProperties());
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d914881..2d53417 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -368,7 +368,7 @@
         // we do not start the service and launch a review activity if the calling app
         // is in the foreground passing it a pending intent to start the service when
         // review is completed.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                     callingUid, service, callerFg, userId)) {
                 return null;
@@ -891,7 +891,7 @@
         // we schedule binding to the service but do not start its process, then
         // we launch a review activity to which is passed a callback to invoke
         // when done to start the bound service's process to completing the binding.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     s.packageName, s.userId)) {
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eb5bf22..0f54d23 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1558,6 +1558,8 @@
     // being called for multiwindow assist in a single session.
     private int mViSessionId = 1000;
 
+    final boolean mPermissionReviewRequired;
+
     final class KillHandler extends Handler {
         static final int KILL_PROCESS_GROUP_MSG = 4000;
 
@@ -2584,6 +2586,9 @@
 
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
 
+        mPermissionReviewRequired = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_permissionReviewRequired);
+
         mHandlerThread = new ServiceThread(TAG,
                 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
         mHandlerThread.start();
@@ -6346,13 +6351,18 @@
             removeLruProcessLocked(app);
             if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
-                try {
-                    IBackupManager bm = IBackupManager.Stub.asInterface(
-                            ServiceManager.getService(Context.BACKUP_SERVICE));
-                    bm.agentDisconnected(app.info.packageName);
-                } catch (RemoteException e) {
-                    // Can't happen; the backup manager is local
-                }
+                mHandler.post(new Runnable() {
+                @Override
+                    public void run(){
+                        try {
+                            IBackupManager bm = IBackupManager.Stub.asInterface(
+                                    ServiceManager.getService(Context.BACKUP_SERVICE));
+                            bm.agentDisconnected(app.info.packageName);
+                        } catch (RemoteException e) {
+                            // Can't happen; the backup manager is local
+                        }
+                    }
+                });
             }
             if (isPendingBroadcastProcessLocked(pid)) {
                 Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
@@ -9522,7 +9532,7 @@
         for (int i = 0; i < procsToKill.size(); i++) {
             ProcessRecord pr = procsToKill.get(i);
             if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                    && pr.curReceiver == null) {
+                    && pr.curReceivers.isEmpty()) {
                 pr.kill("remove task", true);
             } else {
                 // We delay killing processes that are not in the background or running a receiver.
@@ -10741,7 +10751,7 @@
                     // If permissions need a review before any of the app components can run,
                     // we return no provider and launch a review activity if the calling app
                     // is in the foreground.
-                    if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+                    if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
                         if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
                             return null;
                         }
@@ -16761,13 +16771,18 @@
         if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
                     + mBackupTarget.appInfo + " died during backup");
-            try {
-                IBackupManager bm = IBackupManager.Stub.asInterface(
-                        ServiceManager.getService(Context.BACKUP_SERVICE));
-                bm.agentDisconnected(app.info.packageName);
-            } catch (RemoteException e) {
-                // can't happen; backup manager is local
-            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run(){
+                    try {
+                        IBackupManager bm = IBackupManager.Stub.asInterface(
+                                ServiceManager.getService(Context.BACKUP_SERVICE));
+                        bm.agentDisconnected(app.info.packageName);
+                    } catch (RemoteException e) {
+                        // can't happen; backup manager is local
+                    }
+                }
+            });
         }
 
         for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
@@ -18903,26 +18918,28 @@
     // LIFETIME MANAGEMENT
     // =========================================================
 
-    // Returns which broadcast queue the app is the current [or imminent] receiver
-    // on, or 'null' if the app is not an active broadcast recipient.
-    private BroadcastQueue isReceivingBroadcast(ProcessRecord app) {
-        BroadcastRecord r = app.curReceiver;
-        if (r != null) {
-            return r.queue;
+    // Returns whether the app is receiving broadcast.
+    // If receiving, fetch all broadcast queues which the app is
+    // the current [or imminent] receiver on.
+    private boolean isReceivingBroadcastLocked(ProcessRecord app,
+            ArraySet<BroadcastQueue> receivingQueues) {
+        if (!app.curReceivers.isEmpty()) {
+            for (BroadcastRecord r : app.curReceivers) {
+                receivingQueues.add(r.queue);
+            }
+            return true;
         }
 
         // It's not the current receiver, but it might be starting up to become one
-        synchronized (this) {
-            for (BroadcastQueue queue : mBroadcastQueues) {
-                r = queue.mPendingBroadcast;
-                if (r != null && r.curApp == app) {
-                    // found it; report which queue it's in
-                    return queue;
-                }
+        for (BroadcastQueue queue : mBroadcastQueues) {
+            final BroadcastRecord r = queue.mPendingBroadcast;
+            if (r != null && r.curApp == app) {
+                // found it; report which queue it's in
+                receivingQueues.add(queue);
             }
         }
 
-        return null;
+        return !receivingQueues.isEmpty();
     }
 
     Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
@@ -19085,7 +19102,7 @@
         int schedGroup;
         int procState;
         boolean foregroundActivities = false;
-        BroadcastQueue queue;
+        final ArraySet<BroadcastQueue> queues = new ArraySet<BroadcastQueue>();
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
             adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -19099,13 +19116,13 @@
             schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             app.adjType = "instrumentation";
             procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-        } else if ((queue = isReceivingBroadcast(app)) != null) {
+        } else if (isReceivingBroadcastLocked(app, queues)) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
             // It's placed in a sched group based on the nature of the
             // broadcast as reflected by which queue it's active in.
             adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = (queue == mFgBroadcastQueue)
+            schedGroup = (queues.contains(mFgBroadcastQueue))
                     ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
             app.adjType = "broadcast";
             procState = ActivityManager.PROCESS_STATE_RECEIVER;
@@ -20115,7 +20132,7 @@
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                     "Setting sched group of " + app.processName
                     + " to " + app.curSchedGroup);
-            if (app.waitingToKill != null && app.curReceiver == null
+            if (app.waitingToKill != null && app.curReceivers.isEmpty()
                     && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
                 app.kill(app.waitingToKill, true);
                 success = false;
@@ -20985,7 +21002,7 @@
             for (i=mRemovedProcesses.size()-1; i>=0; i--) {
                 final ProcessRecord app = mRemovedProcesses.get(i);
                 if (app.activities.size() == 0
-                        && app.curReceiver == null && app.services.size() == 0) {
+                        && app.curReceivers.isEmpty() && app.services.size() == 0) {
                     Slog.i(
                         TAG, "Exiting empty application process "
                         + app.toShortString() + " ("
@@ -21829,4 +21846,29 @@
             Binder.restoreCallingIdentity(callingId);
         }
     }
+
+    /**
+     * Attach an agent to the specified process (proces name or PID)
+     */
+    public void attachAgent(String process, String path) {
+        try {
+            synchronized (this) {
+                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+                if (proc == null || proc.thread == null) {
+                    throw new IllegalArgumentException("Unknown process: " + process);
+                }
+
+                boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+                if (!isDebuggable) {
+                    if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                        throw new SecurityException("Process not debuggable: " + proc);
+                    }
+                }
+
+                proc.thread.attachAgent(path);
+            }
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Process disappeared");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index adf6d36..2d0ccbd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -66,6 +66,8 @@
                     return runLenientBackgroundCheck(pw);
                 case "get-uid-state":
                     return getUidState(pw);
+                case "attach-agent":
+                    return runAttachAgent(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -183,6 +185,21 @@
         return 0;
     }
 
+    int runAttachAgent(PrintWriter pw) {
+        // TODO: revisit the permissions required for attaching agents
+        mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+                "attach-agent");
+        String process = getNextArgRequired();
+        String agent = getNextArgRequired();
+        String opt;
+        if ((opt = getNextArg()) != null) {
+            pw.println("Error: Unknown option: " + opt);
+            return -1;
+        }
+        mInternal.attachAgent(process, agent);
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -241,6 +258,8 @@
             pw.println("    Optionally controls lenient background check mode, returns current mode.");
             pw.println("  get-uid-state <UID>");
             pw.println("    Gets the process state of an app given its <UID>.");
+            pw.println("  attach-agent <PROCESS> <FILE>");
+            pw.println("    Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 85fa139..5bd6bed 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -410,7 +410,8 @@
         // If permissions need a review before any of the app components can run, we
         // launch the review activity and pass a pending intent to start the activity
         // we are to launching now after the review is completed.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
+        if ((mService.mPermissionReviewRequired
+                || Build.PERMISSIONS_REVIEW_REQUIRED) && aInfo != null) {
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 IIntentSender target = mService.getIntentSenderLocked(
@@ -1540,9 +1541,9 @@
                 == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
             // The caller has requested to completely replace any existing task with its new
             // activity. Well that should not be too hard...
+            intentActivity.task.performClearTaskLocked();
+            intentActivity.task.setIntent(mStartActivity);
             mReuseTask = intentActivity.task;
-            mReuseTask.performClearTaskLocked();
-            mReuseTask.setIntent(mStartActivity);
             // When we clear the task - focus will be adjusted, which will bring another task
             // to top before we launch the activity we need. This will temporary swap their
             // mTaskToReturnTo values and we don't want to overwrite them accidentally.
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a279290..2e1958c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -265,7 +265,7 @@
 
         r.receiver = app.thread.asBinder();
         r.curApp = app;
-        app.curReceiver = r;
+        app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
         mService.updateLruProcessLocked(app, false, null);
         mService.updateOomAdjLocked();
@@ -293,7 +293,7 @@
                         "Process cur broadcast " + r + ": NOT STARTED!");
                 r.receiver = null;
                 r.curApp = null;
-                app.curReceiver = null;
+                app.curReceivers.remove(r);
             }
         }
     }
@@ -389,8 +389,8 @@
         }
         r.receiver = null;
         r.intent.setComponent(null);
-        if (r.curApp != null && r.curApp.curReceiver == r) {
-            r.curApp.curReceiver = null;
+        if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
+            r.curApp.curReceivers.remove(r);
         }
         if (r.curFilter != null) {
             r.curFilter.receiverList.curBroadcast = null;
@@ -618,7 +618,7 @@
         // the broadcast and if the calling app is in the foreground and the broadcast is
         // explicit we launch the review UI passing it a pending intent to send the skipped
         // broadcast.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
             if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
                     filter.owningUserId)) {
                 r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
@@ -643,7 +643,7 @@
                 // things that directly call the IActivityManager API, which
                 // are already core system stuff so don't matter for this.
                 r.curApp = filter.receiverList.app;
-                filter.receiverList.app.curReceiver = r;
+                filter.receiverList.app.curReceivers.add(r);
                 mService.updateOomAdjLocked(r.curApp);
             }
         }
@@ -671,7 +671,7 @@
                 r.curFilter = null;
                 filter.receiverList.curBroadcast = null;
                 if (filter.receiverList.app != null) {
-                    filter.receiverList.app.curReceiver = null;
+                    filter.receiverList.app.curReceivers.remove(r);
                 }
             }
         }
@@ -1124,7 +1124,8 @@
             // the broadcast and if the calling app is in the foreground and the broadcast is
             // explicit we launch the review UI passing it a pending intent to send the skipped
             // broadcast.
-            if (Build.PERMISSIONS_REVIEW_REQUIRED && !skip) {
+            if ((mService.mPermissionReviewRequired
+                    || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
                 if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
                         info.activityInfo.packageName, UserHandle.getUserId(
                                 info.activityInfo.applicationInfo.uid))) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8911a3e..68de95f 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -138,7 +138,7 @@
     Bundle instrumentationArguments;// as given to us
     ComponentName instrumentationResultClass;// copy of instrumentationClass
     boolean usingWrapper;       // Set to true when process was launched with a wrapper attached
-    BroadcastRecord curReceiver;// receiver currently running in the app
+    final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
     long lastWakeTime;          // How long proc held wake lock at last check
     long lastCpuTime;           // How long proc has run CPU at last check
     long curCpuTime;            // How long proc has run CPU most recently
@@ -421,8 +421,11 @@
                 pw.print(prefix); pw.print("  - "); pw.println(conProviders.get(i).toShortString());
             }
         }
-        if (curReceiver != null) {
-            pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
+        if (!curReceivers.isEmpty()) {
+            pw.print(prefix); pw.println("Current Receivers:");
+            for (int i=0; i < curReceivers.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(curReceivers.valueAt(i));
+            }
         }
         if (receivers.size() > 0) {
             pw.print(prefix); pw.println("Receivers:");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7777ae23f..027c722 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3097,14 +3097,28 @@
         boolean success =
             handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
             handleDeviceConnection(connected, inDevice, address, btDeviceName);
-        if (success) {
-            synchronized (mScoClients) {
-                if (connected) {
-                    mBluetoothHeadsetDevice = btDevice;
-                } else {
-                    mBluetoothHeadsetDevice = null;
-                    resetBluetoothSco();
-                }
+
+        if (!success) {
+          return;
+        }
+
+        /* When one BT headset is disconnected while another BT headset
+         * is connected, don't mess with the headset device.
+         */
+        if ((state == BluetoothProfile.STATE_DISCONNECTED ||
+            state == BluetoothProfile.STATE_DISCONNECTING) &&
+            mBluetoothHeadset != null &&
+            mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            Log.w(TAG, "SCO connected through another device, returning");
+            return;
+        }
+
+        synchronized (mScoClients) {
+            if (connected) {
+                mBluetoothHeadsetDevice = btDevice;
+            } else {
+                mBluetoothHeadsetDevice = null;
+                resetBluetoothSco();
             }
         }
     }
@@ -5250,7 +5264,6 @@
                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                                BluetoothProfile.STATE_DISCONNECTED);
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
                 setBtScoDeviceConnectionState(btDevice, state);
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 boolean broadcast = false;
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
similarity index 71%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index 5f66d16..4f68652 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package com.android.server.connectivity;
 
-parcelable PublishConfig;
+import android.os.SystemProperties;
+
+public class MockableSystemProperties {
+    public boolean getBoolean(String key, boolean def) {
+        return SystemProperties.getBoolean(key, def);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 98f34ca..f21d9b7 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -126,7 +126,9 @@
 
     @Override
     // Called concurrently by multiple binder threads.
-    public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs) {
+    // This method must not block or perform long-running operations.
+    public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
+            String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
         maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)",
                 netId, eventType, returnCode, latencyMs));
 
@@ -138,6 +140,14 @@
         batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
     }
 
+    @Override
+    // Called concurrently by multiple binder threads.
+    // This method must not block or perform long-running operations.
+    public synchronized void onConnectEvent(int netId, int latencyMs, String ipAddr, int port,
+            int uid) {
+        maybeVerboseLog(String.format("onConnectEvent(%d, %d)", netId, latencyMs));
+    }
+
     public synchronized void dump(PrintWriter writer) {
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println(TAG + ":");
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 1ca771f..cec4141 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -52,7 +52,6 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -62,6 +61,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.IndentingPrintWriter;
@@ -69,7 +69,6 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
-import com.android.server.IoThread;
 import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.net.BaseNetworkObserver;
@@ -99,6 +98,8 @@
     private final static boolean DBG = false;
     private final static boolean VDBG = false;
 
+    protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
     private static final Class[] messageClasses = {
             Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
     };
@@ -126,6 +127,7 @@
     private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
+    private final MockableSystemProperties mSystemProperties;
 
     private static class TetherState {
         public final TetherInterfaceStateMachine mStateMachine;
@@ -179,18 +181,19 @@
     private boolean mWifiTetherRequested;
 
     public Tethering(Context context, INetworkManagementService nmService,
-            INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+            INetworkStatsService statsService, INetworkPolicyManager policyManager,
+            Looper looper, MockableSystemProperties systemProperties) {
         mContext = context;
         mNMService = nmService;
         mStatsService = statsService;
         mPolicyManager = policyManager;
+        mLooper = looper;
+        mSystemProperties = systemProperties;
 
         mPublicSync = new Object();
 
         mTetherStates = new ArrayMap<>();
 
-        // make our own thread so we don't anr the system
-        mLooper = IoThread.get().getLooper();
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
         mTetherMasterSM.start();
 
@@ -393,10 +396,11 @@
      *
      * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
      */
-    private boolean isTetherProvisioningRequired() {
+    @VisibleForTesting
+    protected boolean isTetherProvisioningRequired() {
         String[] provisionApp = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_mobile_hotspot_provision_app);
-        if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
                 || provisionApp == null) {
             return false;
         }
@@ -896,7 +900,7 @@
                     }
                 } else {
                     mUsbTetherRequested = true;
-                    usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS);
+                    usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
                 }
             } else {
                 final long ident = Binder.clearCallingIdentity();
@@ -906,7 +910,7 @@
                     Binder.restoreCallingIdentity(ident);
                 }
                 if (mRndisEnabled) {
-                    usbManager.setCurrentFunction(null);
+                    usbManager.setCurrentFunction(null, false);
                 }
                 mUsbTetherRequested = false;
             }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 8f8afd5..3b514be 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -286,7 +286,7 @@
                     + mScreenBrightnessDarkConfig + ") to be less than or equal to "
                     + "config_screenBrightnessDim (" + mScreenBrightnessDimConfig + ").");
         }
-        if (mScreenBrightnessDarkConfig > mScreenBrightnessDimConfig) {
+        if (mScreenBrightnessDarkConfig > screenBrightnessSettingMinimum) {
             Slog.w(TAG, "Expected config_screenBrightnessDark ("
                     + mScreenBrightnessDarkConfig + ") to be less than or equal to "
                     + "config_screenBrightnessSettingMinimum ("
diff --git a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
index cca9f10..353f450 100644
--- a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
+++ b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
@@ -99,6 +99,7 @@
     };
     private boolean mSimNeedsEmergencyAffordance;
     private boolean mNetworkNeedsEmergencyAffordance;
+    private boolean mVoiceCapable;
 
     private void requestCellScan() {
         mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget();
@@ -125,8 +126,8 @@
 
     private void updateEmergencyAffordanceNeeded() {
         synchronized (mLock) {
-            mEmergencyAffordanceNeeded = mSimNeedsEmergencyAffordance ||
-                    mNetworkNeedsEmergencyAffordance;
+            mEmergencyAffordanceNeeded = mVoiceCapable && (mSimNeedsEmergencyAffordance ||
+                    mNetworkNeedsEmergencyAffordance);
             Settings.Global.putInt(mContext.getContentResolver(),
                     Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
                     mEmergencyAffordanceNeeded ? 1 : 0);
@@ -157,6 +158,11 @@
     public void onBootPhase(int phase) {
         if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
             mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+            mVoiceCapable = mTelephonyManager.isVoiceCapable();
+            if (!mVoiceCapable) {
+                updateEmergencyAffordanceNeeded();
+                return;
+            }
             mSubscriptionManager = SubscriptionManager.from(mContext);
             HandlerThread thread = new HandlerThread(TAG);
             thread.start();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 85cc124..70560c7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1119,6 +1119,8 @@
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
+    final boolean mPermissionReviewRequired;
+
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
 
@@ -2053,6 +2055,10 @@
         }
 
         mContext = context;
+
+        mPermissionReviewRequired = context.getResources().getBoolean(
+                R.bool.config_permissionReviewRequired);
+
         mFactoryTest = factoryTest;
         mOnlyCore = onlyCore;
         mMetrics = new DisplayMetrics();
@@ -3965,7 +3971,7 @@
             // their permissions as always granted runtime ones since we need
             // to keep the review required permission flag per user while an
             // install permission's state is shared across all users.
-            if (Build.PERMISSIONS_REVIEW_REQUIRED
+            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
                     && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                     && bp.isRuntime()) {
                 return;
@@ -4076,7 +4082,7 @@
             // their permissions as always granted runtime ones since we need
             // to keep the review required permission flag per user while an
             // install permission's state is shared across all users.
-            if (Build.PERMISSIONS_REVIEW_REQUIRED
+            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
                     && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                     && bp.isRuntime()) {
                 return;
@@ -9866,7 +9872,8 @@
                     // their permissions as always granted runtime ones since we need
                     // to keep the review required permission flag per user while an
                     // install permission's state is shared across all users.
-                    if (!appSupportsRuntimePermissions && !Build.PERMISSIONS_REVIEW_REQUIRED) {
+                    if (!appSupportsRuntimePermissions && !mPermissionReviewRequired
+                            && !Build.PERMISSIONS_REVIEW_REQUIRED) {
                         // For legacy apps dangerous permissions are install time ones.
                         grant = GRANT_INSTALL;
                     } else if (origPermissions.hasInstallPermission(bp.name)) {
@@ -9952,7 +9959,7 @@
                                             changedRuntimePermissionUserIds, userId);
                                 }
                                 // If the app supports runtime permissions no need for a review.
-                                if (Build.PERMISSIONS_REVIEW_REQUIRED
+                                if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
                                         && appSupportsRuntimePermissions
                                         && (flags & PackageManager
                                                 .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
@@ -9961,7 +9968,8 @@
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
                                 }
-                            } else if (Build.PERMISSIONS_REVIEW_REQUIRED
+                            } else if ((mPermissionReviewRequired
+                                        || Build.PERMISSIONS_REVIEW_REQUIRED)
                                     && !appSupportsRuntimePermissions) {
                                 // For legacy apps that need a permission review, every new
                                 // runtime permission is granted but it is pending a review.
@@ -16423,7 +16431,7 @@
             // If permission review is enabled and this is a legacy app, mark the
             // permission as requiring a review as this is the initial state.
             int flags = 0;
-            if (Build.PERMISSIONS_REVIEW_REQUIRED
+            if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED)
                     && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
                 flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
             }
@@ -20272,7 +20280,7 @@
         // permissions to keep per user flag state whether review is needed.
         // Hence, if a new user is added we have to propagate dangerous
         // permission grants for these legacy apps.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+        if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {
             updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                     | UPDATE_PERMISSIONS_REPLACE_ALL);
         }
@@ -20726,7 +20734,7 @@
         public boolean isPermissionsReviewRequired(String packageName, int userId) {
             synchronized (mPackages) {
                 // If we do not support permission review, done.
-                if (!Build.PERMISSIONS_REVIEW_REQUIRED) {
+                if (!mPermissionReviewRequired && !Build.PERMISSIONS_REVIEW_REQUIRED) {
                     return false;
                 }
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dfd6dfe..99a6979 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1410,7 +1410,6 @@
         VersionInfo ver = mVersion.get(volumeUuid);
         if (ver == null) {
             ver = new VersionInfo();
-            ver.forceCurrent();
             mVersion.put(volumeUuid, ver);
         }
         return ver;
@@ -2795,8 +2794,8 @@
                             "No settings file; creating initial state");
                     // It's enough to just touch version details to create them
                     // with default values
-                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);
-                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);
+                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
                     return false;
                 }
                 str = new FileInputStream(mSettingsFilename);
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index f7f79f3..02dd53d 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -546,7 +546,7 @@
                     bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
                     if (!bluetoothOff) {
                         Log.w(TAG, "Disabling Bluetooth...");
-                        bluetooth.disable(false);  // disable but don't persist new state
+                        bluetooth.disable(mContext.getPackageName(), false);  // disable but don't persist new state
                     }
                 } catch (RemoteException ex) {
                     Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index bb76449..302f9f6 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -36,6 +37,7 @@
 import android.util.Log;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewZygote;
 
 import com.android.internal.util.XmlUtils;
 
@@ -268,6 +270,24 @@
         return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
     }
 
+    @Override
+    public void setMultiProcessEnabledFromContext(Context context) {
+        boolean enableMultiProcess = false;
+        try {
+            enableMultiProcess = Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
+        } catch (Settings.SettingNotFoundException ex) {
+        }
+        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
+    }
+
+    @Override
+    public void registerContentObserver(Context context, ContentObserver contentObserver) {
+        context.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
+                false, contentObserver);
+    }
+
     // flags declaring we want extra info from the package manager for webview providers
     private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
             | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 7bde37a..2d7a998 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
 import android.webkit.WebViewProviderInfo;
 
 /**
@@ -48,4 +49,7 @@
     public boolean systemIsDebuggable();
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException;
+
+    public void setMultiProcessEnabledFromContext(Context context);
+    public void registerContentObserver(Context context, ContentObserver contentObserver);
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 2cf1722..791fbbc 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,6 +20,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.util.Base64;
 import android.util.Slog;
@@ -42,7 +45,8 @@
 
     private SystemInterface mSystemInterface;
     private WebViewUpdater mWebViewUpdater;
-    private Context mContext;
+    private SettingsObserver mSettingsObserver;
+    final private Context mContext;
 
     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
         mContext = context;
@@ -61,6 +65,10 @@
     void prepareWebViewInSystemServer() {
         updateFallbackStateOnBoot();
         mWebViewUpdater.prepareWebViewInSystemServer();
+
+        // Register for changes in the multiprocess developer option. This has to be done
+        // here, since the update service gets created before the ContentResolver service.
+        mSettingsObserver = new SettingsObserver();
     }
 
     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
@@ -667,4 +675,28 @@
                         & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
     }
 
+    /**
+     * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
+     * the WebViewZygote know, so it can start or stop the zygote process
+     * appropriately.
+     */
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver() {
+            super(new Handler());
+
+            mSystemInterface.registerContentObserver(mContext, this);
+
+            // Push the current value of the setting immediately.
+            notifyZygote();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            notifyZygote();
+        }
+
+        private void notifyZygote() {
+            mSystemInterface.setMultiProcessEnabledFromContext(mContext);
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 58b71f5..18e0f3f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -138,8 +138,8 @@
             "com.android.server.midi.MidiService$Lifecycle";
     private static final String WIFI_SERVICE_CLASS =
             "com.android.server.wifi.WifiService";
-    private static final String WIFI_NAN_SERVICE_CLASS =
-            "com.android.server.wifi.nan.WifiNanService";
+    private static final String WIFI_AWARE_SERVICE_CLASS =
+            "com.android.server.wifi.aware.WifiAwareService";
     private static final String WIFI_P2P_SERVICE_CLASS =
             "com.android.server.wifi.p2p.WifiP2pService";
     private static final String ETHERNET_SERVICE_CLASS =
@@ -820,10 +820,11 @@
                 }
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
-                if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)) {
-                    mSystemServiceManager.startService(WIFI_NAN_SERVICE_CLASS);
+                if (context.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_WIFI_AWARE)) {
+                    mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
                 } else {
-                    Slog.i(TAG, "No Wi-Fi NAN Service (NAN support Not Present)");
+                    Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
                 }
                 mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                 mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
new file mode 100755
index 0000000..7980dc2
--- /dev/null
+++ b/services/tests/runtests.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2016 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.
+
+import os
+import subprocess
+import sys
+
+INSTRUMENTED_PACKAGE_RUNNER = ('com.android.frameworks.servicestests/'
+                               'android.support.test.runner.AndroidJUnitRunner')
+
+PACKAGE_WHITELIST = (
+    'android.net',
+    'com.android.server.connectivity',
+)
+
+COLOR_RED = '\033[0;31m'
+COLOR_NONE ='\033[0m'
+
+def run(shell_command, echo=True):
+    if echo:
+        print '%s + %s%s' % (
+                COLOR_RED,
+                echo if isinstance(echo, str) else shell_command,
+                COLOR_NONE)
+    return subprocess.check_call(shell_command, shell=True)
+
+
+def main():
+    build_top = os.environ.get('ANDROID_BUILD_TOP', None)
+    out_dir = os.environ.get('OUT', None)
+    if build_top is None or out_dir is None:
+        print 'You need to source and lunch before you can use this script'
+        return 1
+
+    print 'Building tests...'
+    run('make -j32 -C %s -f build/core/main.mk '
+        'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top,
+        echo='mmma -j32 %s/frameworks/base/services/tests/servicestests' %
+             build_top)
+
+    print 'Installing tests...'
+    run('adb root')
+    run('adb wait-for-device')
+    apk_path = (
+            '%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' %
+            out_dir)
+    run('adb install -r -g "%s"' % apk_path)
+
+    print 'Running tests...'
+    if len(sys.argv) != 1:
+        run('adb shell am instrument -w %s "%s"' %
+            (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
+        return 0
+
+    # It would be nice if the activity manager accepted a list of packages, but
+    # in lieu of that...
+    for package in PACKAGE_WHITELIST:
+        run('adb shell am instrument -w -e package %s %s' %
+            (package, INSTRUMENTED_PACKAGE_RUNNER))
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java b/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
new file mode 100644
index 0000000..a9f68c8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.os.INetworkManagementService;
+import android.os.PersistableBundle;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringTest {
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+    @Mock private Context mContext;
+    @Mock private INetworkManagementService mNMService;
+    @Mock private INetworkStatsService mStatsService;
+    @Mock private INetworkPolicyManager mPolicyManager;
+    @Mock private MockableSystemProperties mSystemProperties;
+    @Mock private Resources mResources;
+    @Mock private CarrierConfigManager mCarrierConfigManager;
+
+    // Like so many Android system APIs, these cannot be mocked because it is marked final.
+    // We have to use the real versions.
+    private final PersistableBundle mCarrierConfig = new PersistableBundle();
+    private final TestLooper mLooper = new TestLooper();
+
+    private Tethering mTethering;
+
+    @Before public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+                .thenReturn(new String[0]);
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[0]);
+        mTethering = new Tethering(mContext, mNMService, mStatsService, mPolicyManager,
+                                   mLooper.getLooper(), mSystemProperties);
+    }
+
+    private void setupForRequiredProvisioning() {
+        // Produce some acceptable looking provision app setting if requested.
+        when(mResources.getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(PROVISIONING_APP_NAME);
+        // Don't disable tethering provisioning unless requested.
+        when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
+                                          anyBoolean())).thenReturn(false);
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+    }
+
+    @Test
+    public void canRequireProvisioning() {
+        setupForRequiredProvisioning();
+        assertTrue(mTethering.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigManagerMissing() {
+        setupForRequiredProvisioning();
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(null);
+        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+        // We therefore still require provisioning.
+        assertTrue(mTethering.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigMissing() {
+        setupForRequiredProvisioning();
+        when(mCarrierConfigManager.getConfig()).thenReturn(null);
+        // We still have a provisioning app configured, so still require provisioning.
+        assertTrue(mTethering.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void provisioningNotRequiredWhenAppNotFound() {
+        setupForRequiredProvisioning();
+        when(mResources.getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(null);
+        assertTrue(!mTethering.isTetherProvisioningRequired());
+        when(mResources.getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(new String[] {"malformedApp"});
+        assertTrue(!mTethering.isTetherProvisioningRequired());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index e33be40..afa5bac 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
 import android.webkit.WebViewProviderInfo;
 
 import java.util.HashMap;
@@ -113,4 +114,10 @@
     public int getFactoryPackageVersion(String packageName) {
         return 0;
     }
+
+    @Override
+    public void setMultiProcessEnabledFromContext(Context context) {}
+
+    @Override
+    public void registerContentObserver(Context context, ContentObserver contentObserver) {}
 }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index b737033..82c7bdb 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -16,31 +16,42 @@
 
 package com.android.server.webkit;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.Signature;
 import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Base64;
-import android.test.AndroidTestCase;
-
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
 
-import java.util.concurrent.CountDownLatch;
-
 import org.hamcrest.Description;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import org.mockito.Mockito;
 import org.mockito.Matchers;
 import org.mockito.ArgumentMatcher;
 
+import java.util.concurrent.CountDownLatch;
+
 
 /**
  * Tests for WebViewUpdateService
  */
-public class WebViewUpdateServiceTest extends AndroidTestCase {
+// Use MediumTest instead of SmallTest as the implementation of WebViewUpdateService
+// is intended to work on several threads and uses at least one sleep/wait-statement.
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class WebViewUpdateServiceTest {
     private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
 
     private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
@@ -48,11 +59,6 @@
 
     private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
     /**
      * Creates a new instance.
      */
@@ -102,7 +108,7 @@
         // Add (enabled and valid) package infos for each provider
         setEnabledAndValidPackageInfos(webviewPackages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
                 Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
@@ -188,12 +194,27 @@
         assertEquals(expectedPackage, response.packageInfo.packageName);
     }
 
+    /**
+     * The WebView preparation boot phase is run on the main thread (especially on a thread with a
+     * looper) so to avoid bugs where our tests fail because a looper hasn't been attached to the
+     * thread running prepareWebViewInSystemServer we run it on the main thread.
+     */
+    private void runWebViewBootPreparationOnMainSync() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+            }
+        });
+    }
+
 
     // ****************
     // Tests
     // ****************
 
 
+    @Test
     public void testWithSinglePackage() {
         String testPackageName = "test.package.name";
         checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName,
@@ -202,6 +223,7 @@
                             true /*default available*/, false /* fallback */, null)});
     }
 
+    @Test
     public void testDefaultPackageUsedOverNonDefault() {
         String defaultPackage = "defaultPackage";
         String nonDefaultPackage = "nonDefaultPackage";
@@ -211,6 +233,7 @@
         checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
     }
 
+    @Test
     public void testSeveralRelros() {
         String singlePackage = "singlePackage";
         checkCertainPackageUsedAfterWebViewBootPreparation(
@@ -222,6 +245,7 @@
 
     // Ensure that package with valid signatures is chosen rather than package with invalid
     // signatures.
+    @Test
     public void testWithSignatures() {
         String validPackage = "valid package";
         String invalidPackage = "invalid package";
@@ -247,7 +271,7 @@
                     true /* valid */, true /* installed */, new Signature[]{validSignature}
                     , 0 /* updateTime */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
 
         checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
@@ -257,13 +281,14 @@
         assertEquals(validPackage, validPackages[0].packageName);
     }
 
+    @Test
     public void testFailWaitingForRelro() {
         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
             new WebViewProviderInfo("packagename", "", true, true, null)};
         setupWithPackages(packages);
         setEnabledAndValidPackageInfos(packages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
                 Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
@@ -274,12 +299,13 @@
         assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
     }
 
+    @Test
     public void testFailListingEmptyWebviewPackages() {
         WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
         setupWithPackages(packages);
         setEnabledAndValidPackageInfos(packages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
                 Matchers.anyObject());
@@ -288,6 +314,7 @@
         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
     }
 
+    @Test
     public void testFailListingInvalidWebviewPackage() {
         WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
@@ -296,7 +323,7 @@
                 createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */,
                     true /* installed */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
                 Matchers.anyObject());
@@ -315,6 +342,7 @@
     }
 
     // Test that switching provider using changeProviderAndSetting works.
+    @Test
     public void testSwitchingProvider() {
         String firstPackage = "first";
         String secondPackage = "second";
@@ -324,6 +352,7 @@
         checkSwitchingProvider(packages, firstPackage, secondPackage);
     }
 
+    @Test
     public void testSwitchingProviderToNonDefault() {
         String defaultPackage = "defaultPackage";
         String nonDefaultPackage = "nonDefaultPackage";
@@ -344,11 +373,13 @@
     }
 
     // Change provider during relro creation by using changeProviderAndSetting
+    @Test
     public void testSwitchingProviderDuringRelroCreation() {
         checkChangingProviderDuringRelroCreation(true);
     }
 
     // Change provider during relro creation by enabling a provider
+    @Test
     public void testChangingProviderThroughEnablingDuringRelroCreation() {
         checkChangingProviderDuringRelroCreation(false);
     }
@@ -373,7 +404,7 @@
 
         CountDownLatch countdown = new CountDownLatch(1);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
                 Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
@@ -420,10 +451,12 @@
         }
     }
 
+    @Test
     public void testRunFallbackLogicIfEnabled() {
         checkFallbackLogicBeingRun(true);
     }
 
+    @Test
     public void testDontRunFallbackLogicIfDisabled() {
         checkFallbackLogicBeingRun(false);
     }
@@ -439,7 +472,7 @@
         setupWithPackages(packages, fallbackLogicEnabled);
         setEnabledAndValidPackageInfos(packages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
         // Verify that we disable the fallback package if fallback logic enabled, and don't disable
         // the fallback package if that logic is disabled
         if (fallbackLogicEnabled) {
@@ -475,6 +508,7 @@
      * 2. Install non-fallback
      * 3. Fallback should be disabled
      */
+    @Test
     public void testInstallingNonFallbackPackage() {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
@@ -488,7 +522,7 @@
                 createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */,
                     true /* installed */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
         Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
                 Matchers.anyObject(), Matchers.anyObject());
 
@@ -509,6 +543,7 @@
         Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
     }
 
+    @Test
     public void testFallbackChangesEnabledState() {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
@@ -520,7 +555,7 @@
         setupWithPackages(packages, true /* fallbackLogicEnabled */);
         setEnabledAndValidPackageInfos(packages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         // Verify fallback disabled at boot when primary package enabled
         Mockito.verify(mTestSystemImpl).enablePackageForUser(
@@ -558,10 +593,12 @@
         checkPreparationPhasesForPackage(primaryPackage, 2);
     }
 
+    @Test
     public void testAddUserWhenFallbackLogicEnabled() {
         checkAddingNewUser(true);
     }
 
+    @Test
     public void testAddUserWhenFallbackLogicDisabled() {
         checkAddingNewUser(false);
     }
@@ -595,6 +632,7 @@
      * Timing dependent test where we verify that the list of valid webview packages becoming empty
      * at a certain point doesn't crash us or break our state.
      */
+    @Test
     public void testNotifyRelroDoesntCrashIfNoPackages() {
         String firstPackage = "first";
         String secondPackage = "second";
@@ -607,7 +645,7 @@
         // Add (enabled and valid) package infos for each provider
         setEnabledAndValidPackageInfos(packages);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
                 Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
@@ -646,6 +684,7 @@
      * Verify that even if a user-chosen package is removed temporarily we start using it again when
      * it is added back.
      */
+    @Test
     public void testTempRemovePackageDoesntSwitchProviderPermanently() {
         String firstPackage = "first";
         String secondPackage = "second";
@@ -679,6 +718,7 @@
      * Ensure that we update the user-chosen setting across boots if the chosen package is no
      * longer installed and valid.
      */
+    @Test
     public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
         String chosenPackage = "chosenPackage";
         String nonChosenPackage = "non-chosenPackage";
@@ -696,7 +736,7 @@
         // Set user-chosen package
         mTestSystemImpl.updateUserSetting(null, chosenPackage);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         // Verify that we switch the setting to point to the current package
         Mockito.verify(mTestSystemImpl).updateUserSetting(
@@ -706,10 +746,12 @@
         checkPreparationPhasesForPackage(nonChosenPackage, 1);
     }
 
+    @Test
     public void testRecoverFailedListingWebViewPackagesSettingsChange() {
         checkRecoverAfterFailListingWebviewPackages(true);
     }
 
+    @Test
     public void testRecoverFailedListingWebViewPackagesAddedPackage() {
         checkRecoverAfterFailListingWebviewPackages(false);
     }
@@ -756,10 +798,12 @@
         checkPreparationPhasesForPackage(secondPackage, 1);
     }
 
+    @Test
     public void testDontKillIfPackageReplaced() {
         checkDontKillIfPackageRemoved(true);
     }
 
+    @Test
     public void testDontKillIfPackageRemoved() {
         checkDontKillIfPackageRemoved(false);
     }
@@ -793,6 +837,7 @@
                 Mockito.anyObject());
     }
 
+    @Test
     public void testKillIfSettingChanged() {
         String firstPackage = "first";
         String secondPackage = "second";
@@ -814,6 +859,7 @@
      * Test that we kill apps using an old provider when we change the provider setting, even if the
      * new provider is not the one we intended to change to.
      */
+    @Test
     public void testKillIfChangeProviderIncorrectly() {
         String firstPackage = "first";
         String secondPackage = "second";
@@ -831,7 +877,7 @@
         // Start with the setting pointing to the third package
         mTestSystemImpl.updateUserSetting(null, thirdPackage);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
         checkPreparationPhasesForPackage(thirdPackage, 1);
 
         mTestSystemImpl.setPackageInfo(
@@ -849,6 +895,7 @@
 
     // Ensure that the update service uses an uninstalled package if that is the only package
     // available.
+    @Test
     public void testWithSingleUninstalledPackage() {
         String testPackageName = "test.package.name";
         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
@@ -858,15 +905,17 @@
         mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
                     true /* valid */, false /* installed */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
     }
 
+    @Test
     public void testNonhiddenPackageUserOverHidden() {
         checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
     }
 
+    @Test
     public void testInstalledPackageUsedOverUninstalled() {
         checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
     }
@@ -889,16 +938,18 @@
                     true /* valid */, (testUninstalled ? false : true) /* installed */,
                     null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
     }
 
+    @Test
     public void testCantSwitchToHiddenPackage () {
         checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */);
     }
 
 
+    @Test
     public void testCantSwitchToUninstalledPackage () {
         checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */);
     }
@@ -927,7 +978,7 @@
                     null /* signatures */, 0 /* updateTime */,
                     (testHidden ? true : false) /* hidden */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
 
@@ -947,11 +998,13 @@
                 Mockito.argThat(new IsPackageInfoWithName(installedPackage)));
     }
 
+    @Test
     public void testHiddenPackageNotPrioritizedEvenIfChosen() {
         checkNonvisiblePackageNotPrioritizedEvenIfChosen(
                 false /* true == uninstalled, false == hidden */);
     }
 
+    @Test
     public void testUninstalledPackageNotPrioritizedEvenIfChosen() {
         checkNonvisiblePackageNotPrioritizedEvenIfChosen(
                 true /* true == uninstalled, false == hidden */);
@@ -979,7 +1032,7 @@
         // Start with the setting pointing to the uninstalled package
         mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
     }
@@ -988,6 +1041,7 @@
      * Ensures that fallback becomes enabled if the primary package is uninstalled for the current
      * user.
      */
+    @Test
     public void testFallbackEnabledIfPrimaryUninstalled() {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
@@ -1002,7 +1056,7 @@
         mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
                     true /* valid */, true /* installed */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
         // Verify that we enable the fallback package
         Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
                 Mockito.anyObject(), Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */);
@@ -1010,6 +1064,7 @@
         checkPreparationPhasesForPackage(fallbackPackage, 1 /* first preparation phase */);
     }
 
+    @Test
     public void testPreparationRunsIffNewPackage() {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
@@ -1025,7 +1080,7 @@
         mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
                     true /* valid */, true /* installed */));
 
-        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
         Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
@@ -1070,5 +1125,4 @@
 
         checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */);
     }
-
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index df9242d..43b8fa5 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -108,9 +108,8 @@
     private static final int MSG_SYSTEM_READY = 3;
     private static final int MSG_BOOT_COMPLETED = 4;
     private static final int MSG_USER_SWITCHED = 5;
-    private static final int MSG_SET_USB_DATA_UNLOCKED = 6;
-    private static final int MSG_UPDATE_USER_RESTRICTIONS = 7;
-    private static final int MSG_UPDATE_HOST_STATE = 8;
+    private static final int MSG_UPDATE_USER_RESTRICTIONS = 6;
+    private static final int MSG_UPDATE_HOST_STATE = 7;
 
     private static final int AUDIO_MODE_SOURCE = 1;
 
@@ -287,7 +286,7 @@
 
         if (functions != null) {
             mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
-            setCurrentFunctions(functions);
+            setCurrentFunctions(functions, false);
         }
     }
 
@@ -335,14 +334,22 @@
                 // Restore default functions.
                 mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
                         UsbManager.USB_FUNCTION_NONE);
-                if (UsbManager.USB_FUNCTION_NONE.equals(mCurrentFunctions)) {
-                    mCurrentFunctions = UsbManager.USB_FUNCTION_MTP;
-                }
                 mCurrentFunctionsApplied = mCurrentFunctions.equals(
                         SystemProperties.get(USB_STATE_PROPERTY));
                 mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(),
                         UsbManager.USB_FUNCTION_ADB);
-                setEnabledFunctions(null, false);
+
+                /**
+                 * Remove MTP from persistent config, to bring usb to a good state
+                 * after fixes to b/31814300. This block can be removed after the update
+                 */
+                String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
+                if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)) {
+                    SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
+                            UsbManager.removeFunction(persisted, UsbManager.USB_FUNCTION_MTP));
+                }
+
+                setEnabledFunctions(null, false, false);
 
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
@@ -374,6 +381,14 @@
             sendMessage(m);
         }
 
+        public void sendMessage(int what, Object arg, boolean arg1) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.obj = arg;
+            m.arg1 = (arg1 ? 1 : 0);
+            sendMessage(m);
+        }
+
         public void updateState(String state) {
             int connected, configured;
 
@@ -427,29 +442,24 @@
             return waitForState(config);
         }
 
-        private void setUsbDataUnlocked(boolean enable) {
-            if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked: " + enable);
-            mUsbDataUnlocked = enable;
-            updateUsbNotification();
-            updateUsbStateBroadcastIfNeeded();
-            setEnabledFunctions(mCurrentFunctions, true);
-        }
-
         private void setAdbEnabled(boolean enable) {
             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
             if (enable != mAdbEnabled) {
                 mAdbEnabled = enable;
+                String oldFunctions = mCurrentFunctions;
 
-                // Due to the persist.sys.usb.config property trigger, changing adb state requires
-                // persisting default function
-                String oldFunctions = getDefaultFunctions();
-                String newFunctions = applyAdbFunction(oldFunctions);
-                if (!oldFunctions.equals(newFunctions)) {
-                    SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunctions);
+                // Persist the adb setting
+                String newFunction = applyAdbFunction(SystemProperties.get(
+                            USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
+                SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
+
+                // Remove mtp from the config if file transfer is not enabled
+                if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) &&
+                        !mUsbDataUnlocked && enable) {
+                    oldFunctions = UsbManager.USB_FUNCTION_NONE;
                 }
 
-                // After persisting them use the lock-down aware function set
-                setEnabledFunctions(mCurrentFunctions, false);
+                setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked);
                 updateAdbNotification();
             }
 
@@ -461,10 +471,17 @@
         /**
          * Evaluates USB function policies and applies the change accordingly.
          */
-        private void setEnabledFunctions(String functions, boolean forceRestart) {
+        private void setEnabledFunctions(String functions, boolean forceRestart,
+                boolean usbDataUnlocked) {
             if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
                     + "forceRestart=" + forceRestart);
 
+            if (usbDataUnlocked != mUsbDataUnlocked) {
+                mUsbDataUnlocked = usbDataUnlocked;
+                updateUsbNotification();
+                forceRestart = true;
+            }
+
             // Try to set the enabled functions.
             final String oldFunctions = mCurrentFunctions;
             final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
@@ -501,7 +518,8 @@
         }
 
         private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
-            if (functions == null) {
+            if (functions == null || applyAdbFunction(functions)
+                    .equals(UsbManager.USB_FUNCTION_NONE)) {
                 functions = getDefaultFunctions();
             }
             functions = applyAdbFunction(functions);
@@ -566,7 +584,7 @@
                 // make sure accessory mode is off
                 // and restore default functions
                 Slog.d(TAG, "exited USB accessory mode");
-                setEnabledFunctions(null, false);
+                setEnabledFunctions(null, false, false);
 
                 if (mCurrentAccessory != null) {
                     if (mBootCompleted) {
@@ -583,10 +601,6 @@
             if (mBroadcastedIntent == null) {
                 for (String key : keySet) {
                     if (intent.getBooleanExtra(key, false)) {
-                        // MTP function is enabled by default.
-                        if (UsbManager.USB_FUNCTION_MTP.equals(key)) {
-                            continue;
-                        }
                         return true;
                     }
                 }
@@ -699,10 +713,7 @@
                 case MSG_UPDATE_STATE:
                     mConnected = (msg.arg1 == 1);
                     mConfigured = (msg.arg2 == 1);
-                    if (!mConnected) {
-                        // When a disconnect occurs, relock access to sensitive user data
-                        mUsbDataUnlocked = false;
-                    }
+
                     updateUsbNotification();
                     updateAdbNotification();
                     if (UsbManager.containsFunction(mCurrentFunctions,
@@ -710,7 +721,7 @@
                         updateCurrentAccessory();
                     } else if (!mConnected) {
                         // restore defaults when USB is disconnected
-                        setEnabledFunctions(null, false);
+                        setEnabledFunctions(null, false, false);
                     }
                     if (mBootCompleted) {
                         updateUsbStateBroadcastIfNeeded();
@@ -730,13 +741,10 @@
                     break;
                 case MSG_SET_CURRENT_FUNCTIONS:
                     String functions = (String)msg.obj;
-                    setEnabledFunctions(functions, false);
+                    setEnabledFunctions(functions, false, msg.arg1 == 1);
                     break;
                 case MSG_UPDATE_USER_RESTRICTIONS:
-                    setEnabledFunctions(mCurrentFunctions, false);
-                    break;
-                case MSG_SET_USB_DATA_UNLOCKED:
-                    setUsbDataUnlocked(msg.arg1 == 1);
+                    setEnabledFunctions(mCurrentFunctions, false, mUsbDataUnlocked);
                     break;
                 case MSG_SYSTEM_READY:
                     updateUsbNotification();
@@ -764,8 +772,7 @@
                             Slog.v(TAG, "Current user switched to " + mCurrentUser
                                     + "; resetting USB host stack for MTP or PTP");
                             // avoid leaking sensitive data from previous user
-                            mUsbDataUnlocked = false;
-                            setEnabledFunctions(mCurrentFunctions, true);
+                            setEnabledFunctions(mCurrentFunctions, true, false);
                         }
                         mCurrentUser = msg.arg1;
                     }
@@ -944,14 +951,10 @@
         return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function);
     }
 
-    public void setCurrentFunctions(String functions) {
-        if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")");
-        mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
-    }
-
-    public void setUsbDataUnlocked(boolean unlocked) {
-        if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked(" + unlocked + ")");
-        mHandler.sendMessage(MSG_SET_USB_DATA_UNLOCKED, unlocked);
+    public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
+        if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
+				usbDataUnlocked + ")");
+        mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
     }
 
     private void readOemUsbOverrideConfig() {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index d6dbe90..daccc00 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -287,7 +287,7 @@
     }
 
     @Override
-    public void setCurrentFunction(String function) {
+    public void setCurrentFunction(String function, boolean usbDataUnlocked) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
         if (!isSupportedCurrentFunction(function)) {
@@ -297,7 +297,7 @@
         }
 
         if (mDeviceManager != null) {
-            mDeviceManager.setCurrentFunctions(function);
+            mDeviceManager.setCurrentFunctions(function, usbDataUnlocked);
         } else {
             throw new IllegalStateException("USB device mode not supported");
         }
@@ -320,12 +320,6 @@
     }
 
     @Override
-    public void setUsbDataUnlocked(boolean unlocked) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.setUsbDataUnlocked(unlocked);
-    }
-
-    @Override
     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
         mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4d7c304..f177a41 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.telecom.Logging.Session;
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telecom.IConnectionService;
@@ -83,6 +84,32 @@
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
+    // Session Definitions
+    private static final String SESSION_HANDLER = "H.";
+    private static final String SESSION_ADD_CS_ADAPTER = "CS.aCSA";
+    private static final String SESSION_REMOVE_CS_ADAPTER = "CS.rCSA";
+    private static final String SESSION_CREATE_CONN = "CS.crCo";
+    private static final String SESSION_ABORT = "CS.ab";
+    private static final String SESSION_ANSWER = "CS.an";
+    private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+    private static final String SESSION_REJECT = "CS.r";
+    private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
+    private static final String SESSION_SILENCE = "CS.s";
+    private static final String SESSION_DISCONNECT = "CS.d";
+    private static final String SESSION_HOLD = "CS.h";
+    private static final String SESSION_UNHOLD = "CS.u";
+    private static final String SESSION_CALL_AUDIO_SC = "CS.cASC";
+    private static final String SESSION_PLAY_DTMF = "CS.pDT";
+    private static final String SESSION_STOP_DTMF = "CS.sDT";
+    private static final String SESSION_CONFERENCE = "CS.c";
+    private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC";
+    private static final String SESSION_MERGE_CONFERENCE = "CS.mC";
+    private static final String SESSION_SWAP_CONFERENCE = "CS.sC";
+    private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
+    private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
+    private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+    private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
+
     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
     private static final int MSG_CREATE_CONNECTION = 2;
     private static final int MSG_ABORT = 3;
@@ -125,12 +152,30 @@
 
     private final IBinder mBinder = new IConnectionService.Stub() {
         @Override
-        public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
-            mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
+        public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_ADD_CS_ADAPTER);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = adapter;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
-        public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
-            mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
+        public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_REMOVE_CS_ADAPTER);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = adapter;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
@@ -139,136 +184,292 @@
                 String id,
                 ConnectionRequest request,
                 boolean isIncoming,
-                boolean isUnknown) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = connectionManagerPhoneAccount;
-            args.arg2 = id;
-            args.arg3 = request;
-            args.argi1 = isIncoming ? 1 : 0;
-            args.argi2 = isUnknown ? 1 : 0;
-            mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
+                boolean isUnknown,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONN);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = connectionManagerPhoneAccount;
+                args.arg2 = id;
+                args.arg3 = request;
+                args.arg4 = Log.createSubsession();
+                args.argi1 = isIncoming ? 1 : 0;
+                args.argi2 = isUnknown ? 1 : 0;
+                mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void abort(String callId) {
-            mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
+        public void abort(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_ABORT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ABORT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void answerVideo(String callId, int videoState) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.argi1 = videoState;
-            mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
+        public void answerVideo(String callId, int videoState, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_ANSWER_VIDEO);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                args.argi1 = videoState;
+                mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void answer(String callId) {
-            mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
+        public void answer(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_ANSWER);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void reject(String callId) {
-            mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
+        public void reject(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_REJECT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_REJECT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void rejectWithMessage(String callId, String message) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = message;
-            mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();
+        public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = message;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void silence(String callId) {
-            mHandler.obtainMessage(MSG_SILENCE, callId).sendToTarget();
+        public void silence(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_SILENCE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_SILENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void disconnect(String callId) {
-            mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
+        public void disconnect(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_DISCONNECT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_DISCONNECT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void hold(String callId) {
-            mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
+        public void hold(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_HOLD);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_HOLD, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void unhold(String callId) {
-            mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
+        public void unhold(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_UNHOLD);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_UNHOLD, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = callAudioState;
-            mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
+        public void onCallAudioStateChanged(String callId, CallAudioState callAudioState,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CALL_AUDIO_SC);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = callAudioState;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void playDtmfTone(String callId, char digit) {
-            mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
+        public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_PLAY_DTMF);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = digit;
+                args.arg2 = callId;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void stopDtmfTone(String callId) {
-            mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
+        public void stopDtmfTone(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_STOP_DTMF);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_STOP_DTMF_TONE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void conference(String callId1, String callId2) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId1;
-            args.arg2 = callId2;
-            mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+        public void conference(String callId1, String callId2, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CONFERENCE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId1;
+                args.arg2 = callId2;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void splitFromConference(String callId) {
-            mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
+        public void splitFromConference(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_SPLIT_CONFERENCE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void mergeConference(String callId) {
-            mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
+        public void mergeConference(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_MERGE_CONFERENCE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_MERGE_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void swapConference(String callId) {
-            mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
+        public void swapConference(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_SWAP_CONFERENCE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_SWAP_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void onPostDialContinue(String callId, boolean proceed) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.argi1 = proceed ? 1 : 0;
-            mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
+        public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                args.argi1 = proceed ? 1 : 0;
+                mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void pullExternalCall(String callId) {
-            mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, callId).sendToTarget();
+        public void pullExternalCall(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_PULL_EXTERNAL_CALL);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void sendCallEvent(String callId, String event, Bundle extras) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = event;
-            args.arg3 = extras;
-            mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();
+        public void sendCallEvent(String callId, String event, Bundle extras,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_SEND_CALL_EVENT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = event;
+                args.arg3 = extras;
+                args.arg4 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
 
         @Override
-        public void onExtrasChanged(String callId, Bundle extras) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = extras;
-            mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget();
+        public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = extras;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
         }
     };
 
@@ -276,15 +477,35 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_ADD_CONNECTION_SERVICE_ADAPTER:
-                    mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
-                    onAdapterAttached();
+                case MSG_ADD_CONNECTION_SERVICE_ADAPTER: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        IConnectionServiceAdapter adapter = (IConnectionServiceAdapter) args.arg1;
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_ADD_CS_ADAPTER);
+                        mAdapter.addAdapter(adapter);
+                        onAdapterAttached();
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER:
-                    mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj);
+                }
+                case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_REMOVE_CS_ADAPTER);
+                        mAdapter.removeAdapter((IConnectionServiceAdapter) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_CREATE_CONNECTION: {
                     SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
                     try {
                         final PhoneAccountHandle connectionManagerPhoneAccount =
                                 (PhoneAccountHandle) args.arg1;
@@ -294,9 +515,12 @@
                         final boolean isUnknown = args.argi2 == 1;
                         if (!mAreAccountsInitialized) {
                             Log.d(this, "Enqueueing pre-init request %s", id);
-                            mPreInitializationConnectionRequests.add(new Runnable() {
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",
+                                            null /*lock*/) {
                                 @Override
-                                public void run() {
+                                public void loggedRun() {
                                     createConnection(
                                             connectionManagerPhoneAccount,
                                             id,
@@ -304,7 +528,7 @@
                                             isIncoming,
                                             isUnknown);
                                 }
-                            });
+                            }.prepare());
                         } else {
                             createConnection(
                                     connectionManagerPhoneAccount,
@@ -315,122 +539,253 @@
                         }
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
-                case MSG_ABORT:
-                    abort((String) msg.obj);
+                case MSG_ABORT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT);
+                    try {
+                        abort((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_ANSWER:
-                    answer((String) msg.obj);
+                }
+                case MSG_ANSWER: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ANSWER);
+                    try {
+                        answer((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_ANSWER_VIDEO: {
                     SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2,
+                            SESSION_HANDLER + SESSION_ANSWER_VIDEO);
                     try {
                         String callId = (String) args.arg1;
                         int videoState = args.argi1;
                         answerVideo(callId, videoState);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
-                case MSG_REJECT:
-                    reject((String) msg.obj);
+                case MSG_REJECT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
+                    try {
+                        reject((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_REJECT_WITH_MESSAGE: {
                     SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3,
+                            SESSION_HANDLER + SESSION_REJECT_MESSAGE);
                     try {
                         reject((String) args.arg1, (String) args.arg2);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
-                case MSG_DISCONNECT:
-                    disconnect((String) msg.obj);
+                case MSG_DISCONNECT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT);
+                    try {
+                        disconnect((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_SILENCE:
-                    silence((String) msg.obj);
+                }
+                case MSG_SILENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_SILENCE);
+                    try {
+                        silence((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_HOLD:
-                    hold((String) msg.obj);
+                }
+                case MSG_HOLD: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
+                    try {
+                        hold((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_UNHOLD:
-                    unhold((String) msg.obj);
+                }
+                case MSG_UNHOLD: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_UNHOLD);
+                    try {
+                        unhold((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_ON_CALL_AUDIO_STATE_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3,
+                            SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
                     try {
                         String callId = (String) args.arg1;
                         CallAudioState audioState = (CallAudioState) args.arg2;
                         onCallAudioStateChanged(callId, new CallAudioState(audioState));
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
-                case MSG_PLAY_DTMF_TONE:
-                    playDtmfTone((String) msg.obj, (char) msg.arg1);
+                case MSG_PLAY_DTMF_TONE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg3,
+                                SESSION_HANDLER + SESSION_PLAY_DTMF);
+                        playDtmfTone((String) args.arg2, (char) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_STOP_DTMF_TONE:
-                    stopDtmfTone((String) msg.obj);
+                }
+                case MSG_STOP_DTMF_TONE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_STOP_DTMF);
+                        stopDtmfTone((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_CONFERENCE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
+                        Log.continueSession((Session) args.arg3,
+                                SESSION_HANDLER + SESSION_CONFERENCE);
                         String callId1 = (String) args.arg1;
                         String callId2 = (String) args.arg2;
                         conference(callId1, callId2);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
-                case MSG_SPLIT_FROM_CONFERENCE:
-                    splitFromConference((String) msg.obj);
+                case MSG_SPLIT_FROM_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_SPLIT_CONFERENCE);
+                        splitFromConference((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_MERGE_CONFERENCE:
-                    mergeConference((String) msg.obj);
+                }
+                case MSG_MERGE_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_MERGE_CONFERENCE);
+                        mergeConference((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
-                case MSG_SWAP_CONFERENCE:
-                    swapConference((String) msg.obj);
+                }
+                case MSG_SWAP_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_SWAP_CONFERENCE);
+                        swapConference((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
+                }
                 case MSG_ON_POST_DIAL_CONTINUE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_POST_DIAL_CONT);
                         String callId = (String) args.arg1;
                         boolean proceed = (args.argi1 == 1);
                         onPostDialContinue(callId, proceed);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
                 case MSG_PULL_EXTERNAL_CALL: {
-                    pullExternalCall((String) msg.obj);
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_PULL_EXTERNAL_CALL);
+                        pullExternalCall((String) args.arg1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
                     break;
                 }
                 case MSG_SEND_CALL_EVENT: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
+                        Log.continueSession((Session) args.arg4,
+                                SESSION_HANDLER + SESSION_SEND_CALL_EVENT);
                         String callId = (String) args.arg1;
                         String event = (String) args.arg2;
                         Bundle extras = (Bundle) args.arg3;
                         sendCallEvent(callId, event, extras);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
                 case MSG_ON_EXTRAS_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
+                        Log.continueSession((Session) args.arg3,
+                                SESSION_HANDLER + SESSION_EXTRAS_CHANGED);
                         String callId = (String) args.arg1;
                         Bundle extras = (Bundle) args.arg2;
                         handleExtrasChanged(callId, extras);
                     } finally {
                         args.recycle();
+                        Log.endSession();
                     }
                     break;
                 }
@@ -695,7 +1050,7 @@
                 mAdapter.putExtras(id, extras);
             }
         }
-        
+
         public void onExtrasRemoved(Connection c, List<String> keys) {
             String id = mIdByConnection.get(c);
             if (id != null) {
@@ -1026,9 +1381,9 @@
             public void onResult(
                     final List<ComponentName> componentNames,
                     final List<IBinder> services) {
-                mHandler.post(new Runnable() {
+                mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) {
                     @Override
-                    public void run() {
+                    public void loggedRun() {
                         for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
                             mRemoteConnectionManager.addConnectionService(
                                     componentNames.get(i),
@@ -1037,17 +1392,17 @@
                         onAccountsInitialized();
                         Log.d(this, "remote connection services found: " + services);
                     }
-                });
+                }.prepare());
             }
 
             @Override
             public void onError() {
-                mHandler.post(new Runnable() {
+                mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) {
                     @Override
-                    public void run() {
+                    public void loggedRun() {
                         mAreAccountsInitialized = true;
                     }
-                });
+                }.prepare());
             }
         });
     }
@@ -1271,15 +1626,11 @@
      * call created using
      * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
      *
-     * @param connectionManagerPhoneAccount
-     * @param request
-     * @return
-     *
      * @hide
      */
     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
-       return null;
+        return null;
     }
 
     /**
@@ -1492,7 +1843,7 @@
      * @return The call ID.
      */
     private int getNextCallId() {
-        synchronized(mIdSyncRoot) {
+        synchronized (mIdSyncRoot) {
             return ++mId;
         }
     }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index c8cd3c0..616eb8a 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -93,7 +93,8 @@
             ParcelableConnection connection) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.handleCreateConnectionComplete(id, request, connection);
+                adapter.handleCreateConnectionComplete(id, request, connection,
+                        Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -108,7 +109,7 @@
     void setActive(String callId) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setActive(callId);
+                adapter.setActive(callId, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -122,7 +123,7 @@
     void setRinging(String callId) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setRinging(callId);
+                adapter.setRinging(callId, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -136,7 +137,7 @@
     void setDialing(String callId) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setDialing(callId);
+                adapter.setDialing(callId, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -152,7 +153,7 @@
     void setDisconnected(String callId, DisconnectCause disconnectCause) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setDisconnected(callId, disconnectCause);
+                adapter.setDisconnected(callId, disconnectCause, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -166,7 +167,7 @@
     void setOnHold(String callId) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setOnHold(callId);
+                adapter.setOnHold(callId, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -181,7 +182,7 @@
     void setRingbackRequested(String callId, boolean ringback) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setRingbackRequested(callId, ringback);
+                adapter.setRingbackRequested(callId, ringback, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -190,7 +191,7 @@
     void setConnectionCapabilities(String callId, int capabilities) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setConnectionCapabilities(callId, capabilities);
+                adapter.setConnectionCapabilities(callId, capabilities, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -199,7 +200,7 @@
     void setConnectionProperties(String callId, int properties) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setConnectionProperties(callId, properties);
+                adapter.setConnectionProperties(callId, properties, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -217,7 +218,7 @@
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId);
-                adapter.setIsConferenced(callId, conferenceCallId);
+                adapter.setIsConferenced(callId, conferenceCallId, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -232,7 +233,7 @@
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 Log.d(this, "merge failed for call %s", callId);
-                adapter.setConferenceMergeFailed(callId);
+                adapter.setConferenceMergeFailed(callId, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -247,7 +248,7 @@
     void removeCall(String callId) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.removeCall(callId);
+                adapter.removeCall(callId, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -256,7 +257,7 @@
     void onPostDialWait(String callId, String remaining) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.onPostDialWait(callId, remaining);
+                adapter.onPostDialWait(callId, remaining, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -265,7 +266,7 @@
     void onPostDialChar(String callId, char nextChar) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.onPostDialChar(callId, nextChar);
+                adapter.onPostDialChar(callId, nextChar, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -279,7 +280,7 @@
     void addConferenceCall(String callId, ParcelableConference parcelableConference) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.addConferenceCall(callId, parcelableConference);
+                adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -292,7 +293,8 @@
         // Only supported when there is only one adapter.
         if (mAdapters.size() == 1) {
             try {
-                mAdapters.iterator().next().queryRemoteConnectionServices(callback);
+                mAdapters.iterator().next().queryRemoteConnectionServices(callback,
+                        Log.getExternalSession());
             } catch (RemoteException e) {
                 Log.e(this, e, "Exception trying to query for remote CSs");
             }
@@ -311,7 +313,8 @@
             try {
                 adapter.setVideoProvider(
                         callId,
-                        videoProvider == null ? null : videoProvider.getInterface());
+                        videoProvider == null ? null : videoProvider.getInterface(),
+                        Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -326,7 +329,7 @@
     void setIsVoipAudioMode(String callId, boolean isVoip) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setIsVoipAudioMode(callId, isVoip);
+                adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -335,7 +338,7 @@
     void setStatusHints(String callId, StatusHints statusHints) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setStatusHints(callId, statusHints);
+                adapter.setStatusHints(callId, statusHints, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -344,7 +347,7 @@
     void setAddress(String callId, Uri address, int presentation) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setAddress(callId, address, presentation);
+                adapter.setAddress(callId, address, presentation, Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -353,7 +356,8 @@
     void setCallerDisplayName(String callId, String callerDisplayName, int presentation) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setCallerDisplayName(callId, callerDisplayName, presentation);
+                adapter.setCallerDisplayName(callId, callerDisplayName, presentation,
+                        Log.getExternalSession());
             } catch (RemoteException e) {
             }
         }
@@ -374,7 +378,7 @@
         Log.v(this, "setVideoState: %d", videoState);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setVideoState(callId, videoState);
+                adapter.setVideoState(callId, videoState, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -384,7 +388,8 @@
         Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setConferenceableConnections(callId, conferenceableCallIds);
+                adapter.setConferenceableConnections(callId, conferenceableCallIds,
+                        Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -400,7 +405,7 @@
         Log.v(this, "addExistingConnection: %s", callId);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.addExistingConnection(callId, connection);
+                adapter.addExistingConnection(callId, connection, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -416,7 +421,7 @@
         Log.v(this, "putExtras: %s", callId);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.putExtras(callId, extras);
+                adapter.putExtras(callId, extras, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -435,7 +440,7 @@
             try {
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(key, value);
-                adapter.putExtras(callId, bundle);
+                adapter.putExtras(callId, bundle, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -454,7 +459,7 @@
             try {
                 Bundle bundle = new Bundle();
                 bundle.putInt(key, value);
-                adapter.putExtras(callId, bundle);
+                adapter.putExtras(callId, bundle, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -473,7 +478,7 @@
             try {
                 Bundle bundle = new Bundle();
                 bundle.putString(key, value);
-                adapter.putExtras(callId, bundle);
+                adapter.putExtras(callId, bundle, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -488,7 +493,7 @@
         Log.v(this, "removeExtras: %s %s", callId, keys);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.removeExtras(callId, keys);
+                adapter.removeExtras(callId, keys, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
@@ -505,7 +510,7 @@
         Log.v(this, "onConnectionEvent: %s", event);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.onConnectionEvent(callId, event, extras);
+                adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index bf28feb..ba6dbd4 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telecom.Logging.Session;
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -86,46 +87,52 @@
                         mDelegate.handleCreateConnectionComplete(
                                 (String) args.arg1,
                                 (ConnectionRequest) args.arg2,
-                                (ParcelableConnection) args.arg3);
+                                (ParcelableConnection) args.arg3,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_SET_ACTIVE:
-                    mDelegate.setActive((String) msg.obj);
+                    mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
                     break;
                 case MSG_SET_RINGING:
-                    mDelegate.setRinging((String) msg.obj);
+                    mDelegate.setRinging((String) msg.obj, null /*Session.Info*/);
                     break;
                 case MSG_SET_DIALING:
-                    mDelegate.setDialing((String) msg.obj);
+                    mDelegate.setDialing((String) msg.obj, null /*Session.Info*/);
                     break;
                 case MSG_SET_DISCONNECTED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2);
+                        mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_SET_ON_HOLD:
-                    mDelegate.setOnHold((String) msg.obj);
+                    mDelegate.setOnHold((String) msg.obj, null /*Session.Info*/);
                     break;
                 case MSG_SET_RINGBACK_REQUESTED:
-                    mDelegate.setRingbackRequested((String) msg.obj, msg.arg1 == 1);
+                    mDelegate.setRingbackRequested((String) msg.obj, msg.arg1 == 1,
+                            null /*Session.Info*/);
                     break;
                 case MSG_SET_CONNECTION_CAPABILITIES:
-                    mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1);
+                    mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1,
+                            null /*Session.Info*/);
                     break;
                 case MSG_SET_CONNECTION_PROPERTIES:
-                    mDelegate.setConnectionProperties((String) msg.obj, msg.arg1);
+                    mDelegate.setConnectionProperties((String) msg.obj, msg.arg1,
+                            null /*Session.Info*/);
                     break;
                 case MSG_SET_IS_CONFERENCED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2);
+                        mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -135,19 +142,22 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mDelegate.addConferenceCall(
-                                (String) args.arg1, (ParcelableConference) args.arg2);
+                                (String) args.arg1, (ParcelableConference) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_REMOVE_CALL:
-                    mDelegate.removeCall((String) msg.obj);
+                    mDelegate.removeCall((String) msg.obj,
+                            null /*Session.Info*/);
                     break;
                 case MSG_ON_POST_DIAL_WAIT: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2);
+                        mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -156,35 +166,39 @@
                 case MSG_ON_POST_DIAL_CHAR: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.onPostDialChar((String) args.arg1, (char) args.argi1);
+                        mDelegate.onPostDialChar((String) args.arg1, (char) args.argi1,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_QUERY_REMOTE_CALL_SERVICES:
-                    mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
+                    mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj,
+                            null /*Session.Info*/);
                     break;
                 case MSG_SET_VIDEO_STATE:
-                    mDelegate.setVideoState((String) msg.obj, msg.arg1);
+                    mDelegate.setVideoState((String) msg.obj, msg.arg1, null /*Session.Info*/);
                     break;
                 case MSG_SET_VIDEO_CALL_PROVIDER: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mDelegate.setVideoProvider((String) args.arg1,
-                                (IVideoProvider) args.arg2);
+                                (IVideoProvider) args.arg2, null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_SET_IS_VOIP_AUDIO_MODE:
-                    mDelegate.setIsVoipAudioMode((String) msg.obj, msg.arg1 == 1);
+                    mDelegate.setIsVoipAudioMode((String) msg.obj, msg.arg1 == 1,
+                            null /*Session.Info*/);
                     break;
                 case MSG_SET_STATUS_HINTS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2);
+                        mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -193,7 +207,8 @@
                 case MSG_SET_ADDRESS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setAddress((String) args.arg1, (Uri) args.arg2, args.argi1);
+                        mDelegate.setAddress((String) args.arg1, (Uri) args.arg2, args.argi1,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -203,7 +218,8 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mDelegate.setCallerDisplayName(
-                                (String) args.arg1, (String) args.arg2, args.argi1);
+                                (String) args.arg1, (String) args.arg2, args.argi1,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -212,8 +228,8 @@
                 case MSG_SET_CONFERENCEABLE_CONNECTIONS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setConferenceableConnections(
-                                (String) args.arg1, (List<String>) args.arg2);
+                        mDelegate.setConferenceableConnections((String) args.arg1,
+                                (List<String>) args.arg2, null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -222,8 +238,8 @@
                 case MSG_ADD_EXISTING_CONNECTION: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.addExistingConnection(
-                                (String) args.arg1, (ParcelableConnection) args.arg2);
+                        mDelegate.addExistingConnection((String) args.arg1,
+                                (ParcelableConnection) args.arg2, null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -232,7 +248,8 @@
                 case MSG_SET_CONFERENCE_MERGE_FAILED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setConferenceMergeFailed((String) args.arg1);
+                        mDelegate.setConferenceMergeFailed((String) args.arg1,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -241,7 +258,8 @@
                 case MSG_PUT_EXTRAS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.putExtras((String) args.arg1, (Bundle) args.arg2);
+                        mDelegate.putExtras((String) args.arg1, (Bundle) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -250,7 +268,8 @@
                 case MSG_REMOVE_EXTRAS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.removeExtras((String) args.arg1, (List<String>) args.arg2);
+                        mDelegate.removeExtras((String) args.arg1, (List<String>) args.arg2,
+                                null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -260,7 +279,7 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mDelegate.onConnectionEvent((String) args.arg1, (String) args.arg2,
-                                (Bundle) args.arg3);
+                                (Bundle) args.arg3, null /*Session.Info*/);
                     } finally {
                         args.recycle();
                     }
@@ -275,7 +294,8 @@
         public void handleCreateConnectionComplete(
                 String id,
                 ConnectionRequest request,
-                ParcelableConnection connection) {
+                ParcelableConnection connection,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = id;
             args.arg2 = request;
@@ -284,23 +304,23 @@
         }
 
         @Override
-        public void setActive(String connectionId) {
+        public void setActive(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
         }
 
         @Override
-        public void setRinging(String connectionId) {
+        public void setRinging(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget();
         }
 
         @Override
-        public void setDialing(String connectionId) {
+        public void setDialing(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget();
         }
 
         @Override
-        public void setDisconnected(
-                String connectionId, DisconnectCause disconnectCause) {
+        public void setDisconnected(String connectionId, DisconnectCause disconnectCause,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = disconnectCause;
@@ -308,39 +328,43 @@
         }
 
         @Override
-        public void setOnHold(String connectionId) {
+        public void setOnHold(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget();
         }
 
         @Override
-        public void setRingbackRequested(String connectionId, boolean ringback) {
+        public void setRingbackRequested(String connectionId, boolean ringback,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, connectionId)
                     .sendToTarget();
         }
 
         @Override
-        public void setConnectionCapabilities(String connectionId, int connectionCapabilities) {
+        public void setConnectionCapabilities(String connectionId, int connectionCapabilities,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(
                     MSG_SET_CONNECTION_CAPABILITIES, connectionCapabilities, 0, connectionId)
                     .sendToTarget();
         }
 
         @Override
-        public void setConnectionProperties(String connectionId, int connectionProperties) {
+        public void setConnectionProperties(String connectionId, int connectionProperties,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(
                     MSG_SET_CONNECTION_PROPERTIES, connectionProperties, 0, connectionId)
                     .sendToTarget();
         }
 
         @Override
-        public void setConferenceMergeFailed(String callId) {
+        public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
             mHandler.obtainMessage(MSG_SET_CONFERENCE_MERGE_FAILED, args).sendToTarget();
         }
 
         @Override
-        public void setIsConferenced(String callId, String conferenceCallId) {
+        public void setIsConferenced(String callId, String conferenceCallId,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
             args.arg2 = conferenceCallId;
@@ -348,7 +372,8 @@
         }
 
         @Override
-        public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
+        public void addConferenceCall(String callId, ParcelableConference parcelableConference,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
             args.arg2 = parcelableConference;
@@ -356,12 +381,14 @@
         }
 
         @Override
-        public void removeCall(String connectionId) {
+        public void removeCall(String connectionId,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget();
         }
 
         @Override
-        public void onPostDialWait(String connectionId, String remainingDigits) {
+        public void onPostDialWait(String connectionId, String remainingDigits,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = remainingDigits;
@@ -369,7 +396,8 @@
         }
 
         @Override
-        public void onPostDialChar(String connectionId, char nextChar) {
+        public void onPostDialChar(String connectionId, char nextChar,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.argi1 = nextChar;
@@ -377,17 +405,20 @@
         }
 
         @Override
-        public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+        public void queryRemoteConnectionServices(RemoteServiceCallback callback,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
         }
 
         @Override
-        public void setVideoState(String connectionId, int videoState) {
+        public void setVideoState(String connectionId, int videoState,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget();
         }
 
         @Override
-        public void setVideoProvider(String connectionId, IVideoProvider videoProvider) {
+        public void setVideoProvider(String connectionId, IVideoProvider videoProvider,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = videoProvider;
@@ -395,13 +426,15 @@
         }
 
         @Override
-        public final void setIsVoipAudioMode(String connectionId, boolean isVoip) {
+        public final void setIsVoipAudioMode(String connectionId, boolean isVoip,
+                Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0,
                     connectionId).sendToTarget();
         }
 
         @Override
-        public final void setStatusHints(String connectionId, StatusHints statusHints) {
+        public final void setStatusHints(String connectionId, StatusHints statusHints,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = statusHints;
@@ -409,7 +442,8 @@
         }
 
         @Override
-        public final void setAddress(String connectionId, Uri address, int presentation) {
+        public final void setAddress(String connectionId, Uri address, int presentation,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = address;
@@ -419,7 +453,8 @@
 
         @Override
         public final void setCallerDisplayName(
-                String connectionId, String callerDisplayName, int presentation) {
+                String connectionId, String callerDisplayName, int presentation,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = callerDisplayName;
@@ -428,8 +463,8 @@
         }
 
         @Override
-        public final void setConferenceableConnections(
-                String connectionId, List<String> conferenceableConnectionIds) {
+        public final void setConferenceableConnections(String connectionId,
+                List<String> conferenceableConnectionIds, Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = conferenceableConnectionIds;
@@ -437,8 +472,8 @@
         }
 
         @Override
-        public final void addExistingConnection(
-                String connectionId, ParcelableConnection connection) {
+        public final void addExistingConnection(String connectionId,
+                ParcelableConnection connection, Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = connection;
@@ -446,7 +481,7 @@
         }
 
         @Override
-        public final void putExtras(String connectionId, Bundle extras) {
+        public final void putExtras(String connectionId, Bundle extras, Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = extras;
@@ -454,7 +489,8 @@
         }
 
         @Override
-        public final void removeExtras(String connectionId, List<String> keys) {
+        public final void removeExtras(String connectionId, List<String> keys,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = keys;
@@ -462,7 +498,8 @@
         }
 
         @Override
-        public final void onConnectionEvent(String connectionId, String event, Bundle extras) {
+        public final void onConnectionEvent(String connectionId, String event, Bundle extras,
+                Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.arg2 = event;
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index ecda3cd..ced6627 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.content.Context;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.telecom.Logging.EventManager;
@@ -47,13 +48,13 @@
     // Generic tag for all Telecom logging
     @VisibleForTesting
     public static String TAG = "TelecomFramework";
+    public static boolean DEBUG = isLoggable(android.util.Log.DEBUG);
+    public static boolean INFO = isLoggable(android.util.Log.INFO);
+    public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
+    public static boolean WARN = isLoggable(android.util.Log.WARN);
+    public static boolean ERROR = isLoggable(android.util.Log.ERROR);
 
     private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
-    public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
-    public static final boolean INFO = isLoggable(android.util.Log.INFO);
-    public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
-    public static final boolean WARN = isLoggable(android.util.Log.WARN);
-    public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
 
     // Used to synchronize singleton logging lazy initialization
     private static final Object sSingletonSync = new Object();
@@ -175,18 +176,35 @@
      * loggers.
      */
 
+    public static void setSessionContext(Context context) {
+        getSessionManager().setContext(context);
+    }
+
     public static void startSession(String shortMethodName) {
         getSessionManager().startSession(shortMethodName, null);
     }
 
+    public static void startSession(Session.Info info, String shortMethodName) {
+        getSessionManager().startSession(info, shortMethodName, null);
+    }
+
     public static void startSession(String shortMethodName, String callerIdentification) {
         getSessionManager().startSession(shortMethodName, callerIdentification);
     }
 
+    public static void startSession(Session.Info info, String shortMethodName,
+            String callerIdentification) {
+        getSessionManager().startSession(info, shortMethodName, callerIdentification);
+    }
+
     public static Session createSubsession() {
         return getSessionManager().createSubsession();
     }
 
+    public static Session.Info getExternalSession() {
+        return getSessionManager().getExternalSession();
+    }
+
     public static void cancelSubsession(Session subsession) {
         getSessionManager().cancelSubsession(subsession);
     }
@@ -199,6 +217,10 @@
         getSessionManager().endSession();
     }
 
+    public static void registerSessionListener(SessionManager.ISessionListener l) {
+        getSessionManager().registerSessionListener(l);
+    }
+
     public static String getSessionId() {
         // If the Session logger has not been initialized, then there have been no sessions logged.
         // Don't load it now!
@@ -300,7 +322,7 @@
 
     private static MessageDigest sMessageDigest;
 
-    static void initMd5Sum() {
+    public static void initMd5Sum() {
         new AsyncTask<Void, Void, Void>() {
             @Override
             public Void doInBackground(Void... args) {
@@ -318,6 +340,11 @@
 
     public static void setTag(String tag) {
         TAG = tag;
+        DEBUG = isLoggable(android.util.Log.DEBUG);
+        INFO = isLoggable(android.util.Log.INFO);
+        VERBOSE = isLoggable(android.util.Log.VERBOSE);
+        WARN = isLoggable(android.util.Log.WARN);
+        ERROR = isLoggable(android.util.Log.ERROR);
     }
 
     /**
@@ -426,7 +453,7 @@
             msg = (args == null || args.length == 0) ? format
                     : String.format(Locale.US, format, args);
         } catch (IllegalFormatException ife) {
-            e("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
+            e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
                     args.length);
             msg = format + " (An error occurred while formatting the message.)";
         }
diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java
index 8e7a393..2cd1b96 100644
--- a/telecomm/java/android/telecom/Logging/EventManager.java
+++ b/telecomm/java/android/telecom/Logging/EventManager.java
@@ -20,6 +20,7 @@
 import android.telecom.Log;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.text.DateFormat;
@@ -46,6 +47,7 @@
 public class EventManager {
 
     public static final String TAG = "Logging.Events";
+    @VisibleForTesting
     public static final int DEFAULT_EVENTS_TO_CACHE = 10;  // Arbitrarily chosen.
 
     public interface Loggable {
@@ -79,7 +81,7 @@
      * Maps from request events to a list of possible response events. Used to track
      * end-to-end timing for critical user-facing operations in Telecom.
      */
-    public final Map<String, List<TimedEventPair>> requestResponsePairs = new HashMap<>();
+    private final Map<String, List<TimedEventPair>> requestResponsePairs = new HashMap<>();
 
     private static final Object mSync = new Object();
 
@@ -289,7 +291,7 @@
             msg = (args == null || args.length == 0) ? format
                     : String.format(Locale.US, format, args);
         } catch (IllegalFormatException ife) {
-            Log.e("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
+            Log.e(this, ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
                     args.length);
             msg = format + " (An error occurred while formatting the message.)";
         }
@@ -340,6 +342,16 @@
         }
     }
 
+    @VisibleForTesting
+    public LinkedBlockingQueue<EventRecord> getEventRecords() {
+        return mEventRecords;
+    }
+
+    @VisibleForTesting
+    public Map<Loggable, EventRecord> getCallEventRecordMap() {
+        return mCallEventRecordMap;
+    }
+
     private void addEventRecord(EventRecord newRecord) {
         Loggable recordEntry = newRecord.getRecordEntry();
 
@@ -354,8 +366,6 @@
         // Now add a new entry
         mEventRecords.add(newRecord);
         mCallEventRecordMap.put(recordEntry, newRecord);
-
-        // TODO: Add Implementation of this in Telecom for Analytics
         synchronized (mSync) {
             for (EventListener l : mEventListeners) {
                 l.eventRecordAdded(newRecord);
diff --git a/telecomm/java/android/telecom/Logging/Runnable.java b/telecomm/java/android/telecom/Logging/Runnable.java
new file mode 100644
index 0000000..6e81053
--- /dev/null
+++ b/telecomm/java/android/telecom/Logging/Runnable.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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 android.telecom.Logging;
+
+import android.telecom.Log;
+
+/**
+ * Encapsulates session logging in a Runnable to reduce code duplication when continuing subsessions
+ * in a handler/thread.
+ * @hide
+ */
+public abstract class Runnable {
+
+    private Session mSubsession;
+    private final String mSubsessionName;
+    private final Object mLock;
+    private final java.lang.Runnable mRunnable = new java.lang.Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                try {
+                    Log.continueSession(mSubsession, mSubsessionName);
+                    loggedRun();
+                } finally {
+                    if (mSubsession != null) {
+                        Log.endSession();
+                        mSubsession = null;
+                    }
+                }
+            }
+        }
+    };
+
+    /**
+     * Creates a new Telecom Runnable that incorporates Session Logging into it. Useful for carrying
+     * Logging Sessions through different threads as well as through handlers.
+     * @param subsessionName The name that will be used in the Logs to mark this Session
+     * @param lock The synchronization lock that will be used to lock loggedRun().
+     */
+    public Runnable(String subsessionName, Object lock) {
+        if (lock == null) {
+            mLock = new Object();
+        } else {
+            mLock = lock;
+        }
+        mSubsessionName = subsessionName;
+    }
+
+    /**
+     * Return the runnable that will be canceled in the handler queue.
+     * @return Runnable object to cancel.
+     */
+    public final java.lang.Runnable getRunnableToCancel() {
+        return mRunnable;
+    }
+
+    /**
+     * Creates a Runnable and a logging subsession that can be used in a handler/thread. Be sure to
+     * call cancel() if this session is never going to be run (removed from a handler queue, for
+     * for example).
+     * @return A Java Runnable that can be used in a handler queue or thread.
+     */
+    public java.lang.Runnable prepare() {
+        cancel();
+        mSubsession = Log.createSubsession();
+        return mRunnable;
+    }
+
+    /**
+     * This method is used to clean up the active session if the Runnable gets removed from a
+     * handler and is never run.
+     */
+    public void cancel() {
+        synchronized (mLock) {
+            Log.cancelSubsession(mSubsession);
+            mSubsession = null;
+        }
+    }
+
+    /**
+     * The method that will be run in the handler/thread.
+     */
+    abstract public void loggedRun();
+
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/telecomm/java/android/telecom/Logging/Session.aidl
similarity index 83%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to telecomm/java/android/telecom/Logging/Session.aidl
index 5f66d16..68961b6 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/telecomm/java/android/telecom/Logging/Session.aidl
@@ -11,9 +11,12 @@
  * 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.
+ * limitations under the License
  */
 
-package android.net.wifi.nan;
+package android.telecom.Logging;
 
-parcelable PublishConfig;
+/**
+ * {@hide}
+ */
+parcelable Session.Info;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 14f4a0f..c45bd6b 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -17,24 +17,87 @@
 package android.telecom.Logging;
 
 import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Log;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 
 /**
- * The session that stores information about a thread's point of entry into the Telecom code that
- * persists until the thread exits Telecom.
+ * Stores information about a thread's point of entry into that should persist until that thread
+ * exits.
  * @hide
  */
 public class Session {
 
     public static final String START_SESSION = "START_SESSION";
+    public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION";
     public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
     public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION";
     public static final String END_SUBSESSION = "END_SUBSESSION";
     public static final String END_SESSION = "END_SESSION";
 
+    public static final String SUBSESSION_SEPARATION_CHAR = "->";
+    public static final String SESSION_SEPARATION_CHAR_CHILD = "_";
+    public static final String EXTERNAL_INDICATOR = "E-";
+    public static final String TRUNCATE_STRING = "...";
+
+    /**
+     * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
+     * if the Session is canceled.
+     */
     public static final int UNDEFINED = -1;
 
+    public static class Info implements Parcelable {
+        public final String sessionId;
+        public final String methodPath;
+
+        private Info(String id, String path) {
+            sessionId = id;
+            methodPath = path;
+        }
+
+        public static Info getInfo (Session s) {
+            // Create Info based on the truncated method path if the session is external, so we do
+            // not get multiple stacking external sessions (unless we have DEBUG level logging or
+            // lower).
+            return new Info(s.getFullSessionId(), s.getFullMethodPath(
+                    !Log.DEBUG && s.isSessionExternal()));
+        }
+
+        /** Responsible for creating Info objects for deserialized Parcels. */
+        public static final Parcelable.Creator<Info> CREATOR =
+                new Parcelable.Creator<Info> () {
+                    @Override
+                    public Info createFromParcel(Parcel source) {
+                        String id = source.readString();
+                        String methodName = source.readString();
+                        return new Info(id, methodName);
+                    }
+
+                    @Override
+                    public Info[] newArray(int size) {
+                        return new Info[size];
+                    }
+                };
+
+        /** {@inheritDoc} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Writes Info object into a Parcel. */
+        @Override
+        public void writeToParcel(Parcel destination, int flags) {
+            destination.writeString(sessionId);
+            destination.writeString(methodPath);
+        }
+    }
+
     private String mSessionId;
     private String mShortMethodName;
     private long mExecutionStartTimeMs;
@@ -42,6 +105,7 @@
     private Session mParentSession;
     private ArrayList<Session> mChildSessions;
     private boolean mIsCompleted = false;
+    private boolean mIsExternal = false;
     private int mChildCounter = 0;
     // True if this is a subsession that has been started from the same thread as the parent
     // session. This can happen if Log.startSession(...) is called multiple times on the same
@@ -52,8 +116,11 @@
     // Optionally provided info about the method/class/component that started the session in order
     // to make Logging easier. This info will be provided in parentheses along with the session.
     private String mOwnerInfo;
+    // Cache Full Method path so that recursive population of the full method path only needs to
+    // be calculated once.
+    private String mFullMethodPathCache;
 
-    public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID,
+    public Session(String sessionId, String shortMethodName, long startTimeMs,
             boolean isStartedFromActiveSession, String ownerInfo) {
         setSessionId(sessionId);
         setShortMethodName(shortMethodName);
@@ -82,6 +149,14 @@
         mShortMethodName = shortMethodName;
     }
 
+    public void setIsExternal(boolean isExternal) {
+        mIsExternal = isExternal;
+    }
+
+    public boolean isExternal() {
+        return mIsExternal;
+    }
+
     public void setParentSession(Session parentSession) {
         mParentSession = parentSession;
     }
@@ -122,6 +197,15 @@
         return mIsStartedFromActiveSession;
     }
 
+    public Info getInfo() {
+        return Info.getInfo(this);
+    }
+
+    @VisibleForTesting
+    public String getSessionId() {
+        return mSessionId;
+    }
+
     // Mark this session complete. This will be deleted by Log when all subsessions are complete
     // as well.
     public void markSessionCompleted(long executionEndTimeMs) {
@@ -140,27 +224,6 @@
         return String.valueOf(mChildCounter++);
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Session)) {
-            return false;
-        }
-        if (obj == this) {
-            return true;
-        }
-        Session otherSession = (Session) obj;
-        return (mSessionId.equals(otherSession.mSessionId)) &&
-                (mShortMethodName.equals(otherSession.mShortMethodName)) &&
-                mExecutionStartTimeMs == otherSession.mExecutionStartTimeMs &&
-                mParentSession == otherSession.mParentSession &&
-                mChildSessions.equals(otherSession.mChildSessions) &&
-                mIsCompleted == otherSession.mIsCompleted &&
-                mExecutionEndTimeMs == otherSession.mExecutionEndTimeMs &&
-                mChildCounter == otherSession.mChildCounter &&
-                mIsStartedFromActiveSession == otherSession.mIsStartedFromActiveSession &&
-                mOwnerInfo == otherSession.mOwnerInfo;
-    }
-
     // Builds full session id recursively
     private String getFullSessionId() {
         // Cache mParentSession locally to prevent a concurrency problem where
@@ -171,7 +234,15 @@
         if (parentSession == null) {
             return mSessionId;
         } else {
-            return parentSession.getFullSessionId() + "_" + mSessionId;
+            if (Log.VERBOSE) {
+                return parentSession.getFullSessionId() +
+                        // Append "_X" to subsession to show subsession designation.
+                        SESSION_SEPARATION_CHAR_CHILD + mSessionId;
+            } else {
+                // Only worry about the base ID at the top of the tree.
+                return parentSession.getFullSessionId();
+            }
+
         }
     }
 
@@ -203,6 +274,104 @@
         }
     }
 
+    // Recursively concatenate mShortMethodName with the parent Sessions to create full method
+    // path. if truncatePath is set to true, all other external sessions (except for the most
+    // recent) will be truncated to "..."
+    public String getFullMethodPath(boolean truncatePath) {
+        StringBuilder sb = new StringBuilder();
+        getFullMethodPath(sb, truncatePath);
+        return sb.toString();
+    }
+
+    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) {
+        // Return cached value for method path. When returning the truncated path, recalculate the
+        // full path without using the cached value.
+        if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) {
+            sb.append(mFullMethodPathCache);
+            return;
+        }
+        Session parentSession = getParentSession();
+        boolean isSessionStarted = false;
+        if (parentSession != null) {
+            // Check to see if the session has been renamed yet. If it has not, then the session
+            // has not been continued.
+            isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
+            parentSession.getFullMethodPath(sb, truncatePath);
+            sb.append(SUBSESSION_SEPARATION_CHAR);
+        }
+        // Encapsulate the external session's method name so it is obvious what part of the session
+        // is external or truncate it if we do not want the entire history.
+        if (isExternal()) {
+            if (truncatePath) {
+                sb.append(TRUNCATE_STRING);
+            } else {
+                sb.append("(");
+                sb.append(mShortMethodName);
+                sb.append(")");
+            }
+        } else {
+            sb.append(mShortMethodName);
+        }
+        // If we are returning the truncated path, do not save that path as the full path.
+        if (isSessionStarted && !truncatePath) {
+            // Cache this value so that we do not have to do this work next time!
+            // We do not cache the value if the session being evaluated hasn't been continued yet.
+            mFullMethodPathCache = sb.toString();
+        }
+    }
+    // Recursively move to the top of the tree to see if the parent session is external.
+    private boolean isSessionExternal() {
+        if (getParentSession() == null) {
+            return isExternal();
+        } else {
+            return getParentSession().isSessionExternal();
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mSessionId != null ? mSessionId.hashCode() : 0;
+        result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0);
+        result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32));
+        result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32));
+        result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0);
+        result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0);
+        result = 31 * result + (mIsCompleted ? 1 : 0);
+        result = 31 * result + mChildCounter;
+        result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0);
+        result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Session session = (Session) o;
+
+        if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false;
+        if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false;
+        if (mIsCompleted != session.mIsCompleted) return false;
+        if (mChildCounter != session.mChildCounter) return false;
+        if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false;
+        if (mSessionId != null ?
+                !mSessionId.equals(session.mSessionId) : session.mSessionId != null)
+            return false;
+        if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName)
+                : session.mShortMethodName != null)
+            return false;
+        if (mParentSession != null ? !mParentSession.equals(session.mParentSession)
+                : session.mParentSession != null)
+            return false;
+        if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions)
+                : session.mChildSessions != null)
+            return false;
+        return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo)
+                : session.mOwnerInfo == null;
+
+    }
+
     @Override
     public String toString() {
         if (mParentSession != null && mIsStartedFromActiveSession) {
@@ -211,7 +380,7 @@
             return mParentSession.toString();
         } else {
             StringBuilder methodName = new StringBuilder();
-            methodName.append(mShortMethodName);
+            methodName.append(getFullMethodPath(false /*truncatePath*/));
             if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
                 methodName.append("(InCall package: ");
                 methodName.append(mOwnerInfo);
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index a4e8977..949f7b7 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.provider.Settings;
+import android.telecom.Log;
 import android.util.Base64;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -40,12 +42,9 @@
 
     // Currently using 3 letters, So don't exceed 64^3
     private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144;
-
     // This parameter can be overridden in Telecom's Timeouts class.
-    public static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
-
-    private static String LOGGING_TAG = "Logging";
-
+    private static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
+    private static final String LOGGING_TAG = "Logging";
     private static final String TIMEOUTS_PREFIX = "telecom.";
 
     // Synchronized in all method calls
@@ -53,20 +52,26 @@
     private Context mContext;
 
     @VisibleForTesting
-    public ConcurrentHashMap<Integer, Session> sSessionMapper = new ConcurrentHashMap<>(100);
+    public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
     @VisibleForTesting
-    public Handler sSessionCleanupHandler = new Handler(Looper.getMainLooper());
-    @VisibleForTesting
-    public java.lang.Runnable sCleanStaleSessions = () ->
+    public java.lang.Runnable mCleanStaleSessions = () ->
             cleanupStaleSessions(getSessionCleanupTimeoutMs());
+    private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
 
     // Overridden in LogTest to skip query to ContentProvider
     private interface ISessionCleanupTimeoutMs {
         long get();
     }
 
+    // Overridden in tests to provide test Thread IDs
+    public interface ICurrentThreadId {
+        int get();
+    }
+
     @VisibleForTesting
-    public ISessionCleanupTimeoutMs sSessionCleanupTimeoutMs = () -> {
+    public ICurrentThreadId mCurrentThreadId = Process::myTid;
+
+    private ISessionCleanupTimeoutMs mSessionCleanupTimeoutMs = () -> {
         // mContext may be null in some cases, such as testing. For these cases, use the
         // default value.
         if (mContext == null) {
@@ -99,14 +104,29 @@
     }
 
     private long getSessionCleanupTimeoutMs() {
-        return sSessionCleanupTimeoutMs.get();
+        return mSessionCleanupTimeoutMs.get();
     }
 
     private synchronized void resetStaleSessionTimer() {
-        sSessionCleanupHandler.removeCallbacksAndMessages(null);
+        mSessionCleanupHandler.removeCallbacksAndMessages(null);
         // Will be null in Log Testing
-        if (sCleanStaleSessions != null) {
-            sSessionCleanupHandler.postDelayed(sCleanStaleSessions, getSessionCleanupTimeoutMs());
+        if (mCleanStaleSessions != null) {
+            mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs());
+        }
+    }
+
+    /**
+     * Determines whether or not to start a new session or continue an existing session based on
+     * the {@link Session.Info} info passed into startSession. If info is null, a new Session is
+     * created. This code must be accompanied by endSession() at the end of the Session.
+     */
+    public synchronized void startSession(Session.Info info, String shortMethodName,
+            String callerIdentification) {
+        // Start a new session normally if the
+        if(info == null) {
+            startSession(shortMethodName, callerIdentification);
+        } else {
+            startExternalSession(info, shortMethodName);
         }
     }
 
@@ -118,21 +138,60 @@
             String callerIdentification) {
         resetStaleSessionTimer();
         int threadId = getCallingThreadId();
-        Session activeSession = sSessionMapper.get(threadId);
+        Session activeSession = mSessionMapper.get(threadId);
         // We have called startSession within an active session that has not ended... Register this
         // session as a subsession.
         if (activeSession != null) {
             Session childSession = createSubsession(true);
             continueSession(childSession, shortMethodName);
             return;
+        } else {
+            // Only Log that we are starting the parent session.
+            Log.d(LOGGING_TAG, Session.START_SESSION);
         }
         Session newSession = new Session(getNextSessionID(), shortMethodName,
-                System.currentTimeMillis(), threadId, false, callerIdentification);
-        sSessionMapper.put(threadId, newSession);
-
-        android.telecom.Log.v(LOGGING_TAG, Session.START_SESSION);
+                System.currentTimeMillis(), false, callerIdentification);
+        mSessionMapper.put(threadId, newSession);
     }
 
+    /**
+     * Registers an external Session with the Manager using that external Session's sessionInfo.
+     * Log.endSession will still need to be called at the end of the session.
+     * @param sessionInfo Describes the external Session's information.
+     * @param shortMethodName The method name of the new session that is being started.
+     */
+    public synchronized void startExternalSession(Session.Info sessionInfo,
+            String shortMethodName) {
+        if(sessionInfo == null) {
+            return;
+        }
+
+        int threadId = getCallingThreadId();
+        Session threadSession = mSessionMapper.get(threadId);
+        if (threadSession != null) {
+            // We should never get into a situation where there is already an active session AND
+            // an external session is added. We are just using that active session.
+            Log.w(LOGGING_TAG, "trying to start an external session with a session " +
+                    "already active.");
+            return;
+        }
+
+        // Create Session from Info and add to the sessionMapper under this ID.
+        Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
+        Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
+                sessionInfo.methodPath, System.currentTimeMillis(),
+                false /*isStartedFromActiveSession*/, null);
+        externalSession.setIsExternal(true);
+        // Mark the external session as already completed, since we have no way of knowing when
+        // the external session actually has completed.
+        externalSession.markSessionCompleted(Session.UNDEFINED);
+        // Track the external session with the SessionMapper so that we can create and continue
+        // an active subsession based on it.
+        mSessionMapper.put(threadId, externalSession);
+        // Create a subsession from this external Session parent node
+        Session childSession = createSubsession();
+        continueSession(childSession, shortMethodName);
+    }
 
     /**
      * Notifies the logging system that a subsession will be run at a later point and
@@ -145,40 +204,57 @@
 
     private synchronized Session createSubsession(boolean isStartedFromActiveSession) {
         int threadId = getCallingThreadId();
-        Session threadSession = sSessionMapper.get(threadId);
+        Session threadSession = mSessionMapper.get(threadId);
         if (threadSession == null) {
-            android.telecom.Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
+            Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
                     "active.");
             return null;
         }
         // Start execution time of the session will be overwritten in continueSession(...).
         Session newSubsession = new Session(threadSession.getNextChildId(),
-                threadSession.getShortMethodName(), System.currentTimeMillis(), threadId,
+                threadSession.getShortMethodName(), System.currentTimeMillis(),
                 isStartedFromActiveSession, null);
         threadSession.addChild(newSubsession);
         newSubsession.setParentSession(threadSession);
 
         if (!isStartedFromActiveSession) {
-            android.telecom.Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
+            Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
                     newSubsession.toString());
         } else {
-            android.telecom.Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
+            Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
                     " (Invisible subsession)");
         }
         return newSubsession;
     }
 
     /**
+     * Retrieve the information of the currently active Session. This information is parcelable and
+     * is used to create an external Session ({@link #startExternalSession(Session.Info, String)}).
+     * If there is no Session active, this method will return null.
+     */
+    public synchronized Session.Info getExternalSession() {
+        int threadId = getCallingThreadId();
+        Session threadSession = mSessionMapper.get(threadId);
+        if (threadSession == null) {
+            Log.d(LOGGING_TAG, "Log.getExternalSession was called with no session " +
+                    "active.");
+            return null;
+        }
+
+        return threadSession.getInfo();
+    }
+
+    /**
      * Cancels a subsession that had Log.createSubsession() called on it, but will never have
      * Log.continueSession(...) called on it due to an error. Allows the subsession to be cleaned
-     * gracefully instead of being removed by the sSessionCleanupHandler forcefully later.
+     * gracefully instead of being removed by the mSessionCleanupHandler forcefully later.
      */
     public synchronized void cancelSubsession(Session subsession) {
         if (subsession == null) {
             return;
         }
 
-        subsession.markSessionCompleted(0);
+        subsession.markSessionCompleted(Session.UNDEFINED);
         endParentSessions(subsession);
     }
 
@@ -192,21 +268,20 @@
             return;
         }
         resetStaleSessionTimer();
-        String callingMethodName = subsession.getShortMethodName();
-        subsession.setShortMethodName(callingMethodName + "->" + shortMethodName);
+        subsession.setShortMethodName(shortMethodName);
         subsession.setExecutionStartTimeMs(System.currentTimeMillis());
         Session parentSession = subsession.getParentSession();
         if (parentSession == null) {
-            android.telecom.Log.d(LOGGING_TAG, "Log.continueSession was called with no session " +
-                    "active for method %s.", shortMethodName);
+            Log.i(LOGGING_TAG, "Log.continueSession was called with no session " +
+                    "active for method " + shortMethodName);
             return;
         }
 
-        sSessionMapper.put(getCallingThreadId(), subsession);
+        mSessionMapper.put(getCallingThreadId(), subsession);
         if (!subsession.isStartedFromActiveSession()) {
-            android.telecom.Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
+            Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
         } else {
-            android.telecom.Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
+            Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
                     " (Invisible Subsession) with Method " + shortMethodName);
         }
     }
@@ -217,30 +292,30 @@
      */
     public synchronized void endSession() {
         int threadId = getCallingThreadId();
-        Session completedSession = sSessionMapper.get(threadId);
+        Session completedSession = mSessionMapper.get(threadId);
         if (completedSession == null) {
-            android.telecom.Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
+            Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
             return;
         }
 
         completedSession.markSessionCompleted(System.currentTimeMillis());
         if (!completedSession.isStartedFromActiveSession()) {
-            android.telecom.Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
+            Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
                     completedSession.getLocalExecutionTime() + " mS)");
         } else {
-            android.telecom.Log.v(LOGGING_TAG, Session.END_SUBSESSION +
+            Log.v(LOGGING_TAG, Session.END_SUBSESSION +
                     " (Invisible Subsession) (dur: " + completedSession.getLocalExecutionTime() +
                     " ms)");
         }
         // Remove after completed so that reference still exists for logging the end events
         Session parentSession = completedSession.getParentSession();
-        sSessionMapper.remove(threadId);
+        mSessionMapper.remove(threadId);
         endParentSessions(completedSession);
         // If this subsession was started from a parent session using Log.startSession, return the
         // ThreadID back to the parent after completion.
         if (parentSession != null && !parentSession.isSessionCompleted() &&
                 completedSession.isStartedFromActiveSession()) {
-            sSessionMapper.put(threadId, parentSession);
+            mSessionMapper.put(threadId, parentSession);
         }
     }
 
@@ -251,28 +326,39 @@
         if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
             return;
         }
-
         Session parentSession = subsession.getParentSession();
         if (parentSession != null) {
             subsession.setParentSession(null);
             parentSession.removeChild(subsession);
+            // Report the child session of the external session as being complete to the listeners,
+            // not the external session itself.
+            if (parentSession.isExternal()) {
+                long fullSessionTimeMs =
+                        System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
+                notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
+            }
             endParentSessions(parentSession);
         } else {
             // All of the subsessions have been completed and it is time to report on the full
             // running time of the session.
             long fullSessionTimeMs =
                     System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
-            android.telecom.Log.v(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
+            Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
                     + " ms): " + subsession.toString());
-            // TODO: Add analytics hook
-            for (ISessionListener l : mSessionListeners) {
-                l.sessionComplete(subsession.getShortMethodName(), fullSessionTimeMs);
+            if (!subsession.isExternal()) {
+                notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
             }
         }
     }
 
+    private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) {
+        for (ISessionListener l : mSessionListeners) {
+            l.sessionComplete(methodName, sessionTimeMs);
+        }
+    }
+
     public String getSessionId() {
-        Session currentSession = sSessionMapper.get(getCallingThreadId());
+        Session currentSession = mSessionMapper.get(getCallingThreadId());
         return currentSession != null ? currentSession.toString() : "";
     }
 
@@ -291,24 +377,22 @@
         return getBase64Encoding(nextId);
     }
 
-    @VisibleForTesting
-    public synchronized void restartSessionCounter() {
+    private synchronized void restartSessionCounter() {
         sCodeEntryCounter = 0;
     }
 
-    @VisibleForTesting
-    public String getBase64Encoding(int number) {
+    private String getBase64Encoding(int number) {
         byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array();
         idByteArray = Arrays.copyOfRange(idByteArray, 2, 4);
         return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING);
     }
 
-    public int getCallingThreadId() {
-        return android.os.Process.myTid();
+    private int getCallingThreadId() {
+        return mCurrentThreadId.get();
     }
 
     @VisibleForTesting
-    private synchronized void cleanupStaleSessions(long timeoutMs) {
+    public synchronized void cleanupStaleSessions(long timeoutMs) {
         String logMessage = "Stale Sessions Cleaned:\n";
         boolean isSessionsStale = false;
         long currentTimeMs = System.currentTimeMillis();
@@ -317,7 +401,7 @@
         // If this occurs, then there is most likely a Session active that never had
         // Log.endSession called on it.
         for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it =
-             sSessionMapper.entrySet().iterator(); it.hasNext(); ) {
+             mSessionMapper.entrySet().iterator(); it.hasNext(); ) {
             ConcurrentHashMap.Entry<Integer, Session> entry = it.next();
             Session session = entry.getValue();
             if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) {
@@ -327,9 +411,9 @@
             }
         }
         if (isSessionsStale) {
-            android.telecom.Log.w(LOGGING_TAG, logMessage);
+            Log.w(LOGGING_TAG, logMessage);
         } else {
-            android.telecom.Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
+            Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
         }
     }
 
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index dbc2b0c..615ae49 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -114,7 +114,10 @@
     public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
 
     /**
-     * Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
+     * Flag indicating that this {@code PhoneAccount} is currently able to place video calls.
+     * <p>
+     * See also {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING} which indicates whether the
+     * {@code PhoneAccount} supports placing video calls.
      * <p>
      * See {@link #getCapabilities}
      */
@@ -179,6 +182,23 @@
     public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
 
     /**
+     * Flag indicating that this {@link PhoneAccount} supports video calling.
+     * This is not an indication that the {@link PhoneAccount} is currently able to make a video
+     * call, but rather that it has the ability to make video calls (but not necessarily at this
+     * time).
+     * <p>
+     * Whether a {@link PhoneAccount} can make a video call is ultimately controlled by
+     * {@link #CAPABILITY_VIDEO_CALLING}, which indicates whether the {@link PhoneAccount} is
+     * currently capable of making a video call.  Consider a case where, for example, a
+     * {@link PhoneAccount} supports making video calls (e.g.
+     * {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING}), but a current lack of network connectivity
+     * prevents video calls from being made (e.g. {@link #CAPABILITY_VIDEO_CALLING}).
+     * <p>
+     * See {@link #getCapabilities}
+     */
+    public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
+
+    /**
      * URI scheme for telephone number URIs.
      */
     public static final String SCHEME_TEL = "tel";
@@ -716,6 +736,9 @@
      */
     private String capabilitiesToString(int capabilities) {
         StringBuilder sb = new StringBuilder();
+        if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
+            sb.append("SuppVideo ");
+        }
         if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
             sb.append("Video ");
         }
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index bf6038a..6e63621 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -397,7 +397,7 @@
      */
     public void disconnect() {
         try {
-            mConnectionService.disconnect(mId);
+            mConnectionService.disconnect(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -412,7 +412,7 @@
     public void separate(RemoteConnection connection) {
         if (mChildConnections.contains(connection)) {
             try {
-                mConnectionService.splitFromConference(connection.getId());
+                mConnectionService.splitFromConference(connection.getId(), null /*Session.Info*/);
             } catch (RemoteException e) {
             }
         }
@@ -430,7 +430,7 @@
      */
     public void merge() {
         try {
-            mConnectionService.mergeConference(mId);
+            mConnectionService.mergeConference(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -446,7 +446,7 @@
      */
     public void swap() {
         try {
-            mConnectionService.swapConference(mId);
+            mConnectionService.swapConference(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -456,7 +456,7 @@
      */
     public void hold() {
         try {
-            mConnectionService.hold(mId);
+            mConnectionService.hold(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -466,7 +466,7 @@
      */
     public void unhold() {
         try {
-            mConnectionService.unhold(mId);
+            mConnectionService.unhold(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -489,7 +489,7 @@
      */
     public void playDtmfTone(char digit) {
         try {
-            mConnectionService.playDtmfTone(mId, digit);
+            mConnectionService.playDtmfTone(mId, digit, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -501,7 +501,7 @@
      */
     public void stopDtmfTone() {
         try {
-            mConnectionService.stopDtmfTone(mId);
+            mConnectionService.stopDtmfTone(mId, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
@@ -526,7 +526,7 @@
      */
     public void setCallAudioState(CallAudioState state) {
         try {
-            mConnectionService.onCallAudioStateChanged(mId, state);
+            mConnectionService.onCallAudioStateChanged(mId, state, null /*Session.Info*/);
         } catch (RemoteException e) {
         }
     }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 8e06659db..af61f77 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -845,7 +845,7 @@
     public void abort() {
         try {
             if (mConnected) {
-                mConnectionService.abort(mConnectionId);
+                mConnectionService.abort(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -857,7 +857,7 @@
     public void answer() {
         try {
             if (mConnected) {
-                mConnectionService.answer(mConnectionId);
+                mConnectionService.answer(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -871,7 +871,7 @@
     public void answer(int videoState) {
         try {
             if (mConnected) {
-                mConnectionService.answerVideo(mConnectionId, videoState);
+                mConnectionService.answerVideo(mConnectionId, videoState, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -883,7 +883,7 @@
     public void reject() {
         try {
             if (mConnected) {
-                mConnectionService.reject(mConnectionId);
+                mConnectionService.reject(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -895,7 +895,7 @@
     public void hold() {
         try {
             if (mConnected) {
-                mConnectionService.hold(mConnectionId);
+                mConnectionService.hold(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -907,7 +907,7 @@
     public void unhold() {
         try {
             if (mConnected) {
-                mConnectionService.unhold(mConnectionId);
+                mConnectionService.unhold(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -919,7 +919,7 @@
     public void disconnect() {
         try {
             if (mConnected) {
-                mConnectionService.disconnect(mConnectionId);
+                mConnectionService.disconnect(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -937,7 +937,7 @@
     public void playDtmfTone(char digit) {
         try {
             if (mConnected) {
-                mConnectionService.playDtmfTone(mConnectionId, digit);
+                mConnectionService.playDtmfTone(mConnectionId, digit, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -953,7 +953,7 @@
     public void stopDtmfTone() {
         try {
             if (mConnected) {
-                mConnectionService.stopDtmfTone(mConnectionId);
+                mConnectionService.stopDtmfTone(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -983,7 +983,8 @@
     public void postDialContinue(boolean proceed) {
         try {
             if (mConnected) {
-                mConnectionService.onPostDialContinue(mConnectionId, proceed);
+                mConnectionService.onPostDialContinue(mConnectionId, proceed,
+                        null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -998,7 +999,7 @@
     public void pullExternalCall() {
         try {
             if (mConnected) {
-                mConnectionService.pullExternalCall(mConnectionId);
+                mConnectionService.pullExternalCall(mConnectionId, null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
@@ -1025,7 +1026,8 @@
     public void setCallAudioState(CallAudioState state) {
         try {
             if (mConnected) {
-                mConnectionService.onCallAudioStateChanged(mConnectionId, state);
+                mConnectionService.onCallAudioStateChanged(mConnectionId, state,
+                        null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
         }
diff --git a/telecomm/java/android/telecom/RemoteConnectionManager.java b/telecomm/java/android/telecom/RemoteConnectionManager.java
index 0366509..0322218 100644
--- a/telecomm/java/android/telecom/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecom/RemoteConnectionManager.java
@@ -76,7 +76,7 @@
     public void conferenceRemoteConnections(RemoteConnection a, RemoteConnection b) {
         if (a.getConnectionService() == b.getConnectionService()) {
             try {
-                a.getConnectionService().conference(a.getId(), b.getId());
+                a.getConnectionService().conference(a.getId(), b.getId(), null /*Session.Info*/);
             } catch (RemoteException e) {
             }
         } else {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 21a7706..18b56c9 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -21,6 +21,7 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.telecom.Logging.Session;
 
 import com.android.internal.telecom.IConnectionService;
 import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -54,7 +55,8 @@
         public void handleCreateConnectionComplete(
                 String id,
                 ConnectionRequest request,
-                ParcelableConnection parcel) {
+                ParcelableConnection parcel,
+                Session.Info info) {
             RemoteConnection connection =
                     findConnectionForAction(id, "handleCreateConnectionSuccessful");
             if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
@@ -95,7 +97,7 @@
         }
 
         @Override
-        public void setActive(String callId) {
+        public void setActive(String callId, Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setActive")
                         .setState(Connection.STATE_ACTIVE);
@@ -106,19 +108,20 @@
         }
 
         @Override
-        public void setRinging(String callId) {
+        public void setRinging(String callId, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setRinging")
                     .setState(Connection.STATE_RINGING);
         }
 
         @Override
-        public void setDialing(String callId) {
+        public void setDialing(String callId, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setDialing")
                     .setState(Connection.STATE_DIALING);
         }
 
         @Override
-        public void setDisconnected(String callId, DisconnectCause disconnectCause) {
+        public void setDisconnected(String callId, DisconnectCause disconnectCause,
+                Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setDisconnected")
                         .setDisconnected(disconnectCause);
@@ -129,7 +132,7 @@
         }
 
         @Override
-        public void setOnHold(String callId) {
+        public void setOnHold(String callId, Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setOnHold")
                         .setState(Connection.STATE_HOLDING);
@@ -140,13 +143,14 @@
         }
 
         @Override
-        public void setRingbackRequested(String callId, boolean ringing) {
+        public void setRingbackRequested(String callId, boolean ringing, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setRingbackRequested")
                     .setRingbackRequested(ringing);
         }
 
         @Override
-        public void setConnectionCapabilities(String callId, int connectionCapabilities) {
+        public void setConnectionCapabilities(String callId, int connectionCapabilities,
+                Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setConnectionCapabilities")
                         .setConnectionCapabilities(connectionCapabilities);
@@ -157,7 +161,8 @@
         }
 
         @Override
-        public void setConnectionProperties(String callId, int connectionProperties) {
+        public void setConnectionProperties(String callId, int connectionProperties,
+                Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setConnectionProperties")
                         .setConnectionProperties(connectionProperties);
@@ -168,7 +173,8 @@
         }
 
         @Override
-        public void setIsConferenced(String callId, String conferenceCallId) {
+        public void setIsConferenced(String callId, String conferenceCallId,
+                Session.Info sessionInfo) {
             // Note: callId should not be null; conferenceCallId may be null
             RemoteConnection connection =
                     findConnectionForAction(callId, "setIsConferenced");
@@ -189,7 +195,7 @@
         }
 
         @Override
-        public void setConferenceMergeFailed(String callId) {
+        public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
             // Nothing to do here.
             // The event has already been handled and there is no state to update
             // in the underlying connection or conference objects
@@ -197,8 +203,7 @@
 
         @Override
         public void addConferenceCall(
-                final String callId,
-                ParcelableConference parcel) {
+                final String callId, ParcelableConference parcel, Session.Info sessionInfo) {
             RemoteConference conference = new RemoteConference(callId,
                     mOutgoingConnectionServiceRpc);
 
@@ -231,7 +236,7 @@
         }
 
         @Override
-        public void removeCall(String callId) {
+        public void removeCall(String callId, Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "removeCall")
                         .setDestroyed();
@@ -242,24 +247,26 @@
         }
 
         @Override
-        public void onPostDialWait(String callId, String remaining) {
+        public void onPostDialWait(String callId, String remaining, Session.Info sessionInfo) {
             findConnectionForAction(callId, "onPostDialWait")
                     .setPostDialWait(remaining);
         }
 
         @Override
-        public void onPostDialChar(String callId, char nextChar) {
+        public void onPostDialChar(String callId, char nextChar, Session.Info sessionInfo) {
             findConnectionForAction(callId, "onPostDialChar")
                     .onPostDialChar(nextChar);
         }
 
         @Override
-        public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+        public void queryRemoteConnectionServices(RemoteServiceCallback callback,
+                Session.Info sessionInfo) {
             // Not supported from remote connection service.
         }
 
         @Override
-        public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+        public void setVideoProvider(String callId, IVideoProvider videoProvider,
+                Session.Info sessionInfo) {
             RemoteConnection.VideoProvider remoteVideoProvider = null;
             if (videoProvider != null) {
                 remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
@@ -269,32 +276,34 @@
         }
 
         @Override
-        public void setVideoState(String callId, int videoState) {
+        public void setVideoState(String callId, int videoState, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setVideoState")
                     .setVideoState(videoState);
         }
 
         @Override
-        public void setIsVoipAudioMode(String callId, boolean isVoip) {
+        public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setIsVoipAudioMode")
                     .setIsVoipAudioMode(isVoip);
         }
 
         @Override
-        public void setStatusHints(String callId, StatusHints statusHints) {
+        public void setStatusHints(String callId, StatusHints statusHints,
+                Session.Info sessionInfo) {
             findConnectionForAction(callId, "setStatusHints")
                     .setStatusHints(statusHints);
         }
 
         @Override
-        public void setAddress(String callId, Uri address, int presentation) {
+        public void setAddress(String callId, Uri address, int presentation,
+                Session.Info sessionInfo) {
             findConnectionForAction(callId, "setAddress")
                     .setAddress(address, presentation);
         }
 
         @Override
         public void setCallerDisplayName(String callId, String callerDisplayName,
-                int presentation) {
+                int presentation, Session.Info sessionInfo) {
             findConnectionForAction(callId, "setCallerDisplayName")
                     .setCallerDisplayName(callerDisplayName, presentation);
         }
@@ -305,8 +314,8 @@
         }
 
         @Override
-        public final void setConferenceableConnections(
-                String callId, List<String> conferenceableConnectionIds) {
+        public final void setConferenceableConnections(String callId,
+                List<String> conferenceableConnectionIds, Session.Info sessionInfo) {
             List<RemoteConnection> conferenceable = new ArrayList<>();
             for (String id : conferenceableConnectionIds) {
                 if (mConnectionById.containsKey(id)) {
@@ -324,7 +333,8 @@
         }
 
         @Override
-        public void addExistingConnection(String callId, ParcelableConnection connection) {
+        public void addExistingConnection(String callId, ParcelableConnection connection,
+                Session.Info sessionInfo) {
             // TODO: add contents of this method
             RemoteConnection remoteConnction = new RemoteConnection(callId,
                     mOutgoingConnectionServiceRpc, connection);
@@ -333,7 +343,7 @@
         }
 
         @Override
-        public void putExtras(String callId, Bundle extras) {
+        public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) {
             if (hasConnection(callId)) {
                 findConnectionForAction(callId, "putExtras").putExtras(extras);
             } else {
@@ -342,7 +352,7 @@
         }
 
         @Override
-        public void removeExtras(String callId, List<String> keys) {
+        public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) {
             if (hasConnection(callId)) {
                 findConnectionForAction(callId, "removeExtra").removeExtras(keys);
             } else {
@@ -351,7 +361,8 @@
         }
 
         @Override
-        public void onConnectionEvent(String callId, String event, Bundle extras) {
+        public void onConnectionEvent(String callId, String event, Bundle extras,
+                Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "onConnectionEvent").onConnectionEvent(event,
                         extras);
@@ -409,7 +420,8 @@
                 request.getVideoState());
         try {
             if (mConnectionById.isEmpty()) {
-                mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub());
+                mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(),
+                        null /*Session.Info*/);
             }
             RemoteConnection connection =
                     new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest);
@@ -420,7 +432,8 @@
                     id,
                     newRequest,
                     isIncoming,
-                    false /* isUnknownCall */);
+                    false /* isUnknownCall */,
+                    null /*Session.info*/);
             connection.registerCallback(new RemoteConnection.Callback() {
                 @Override
                 public void onDestroyed(RemoteConnection connection) {
@@ -460,7 +473,8 @@
     private void maybeDisconnectAdapter() {
         if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) {
             try {
-                mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub());
+                mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub(),
+                        null /*Session.info*/);
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index a4c1798..8a27675 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.ConnectionRequest;
+import android.telecom.Logging.Session;
 import android.telecom.PhoneAccountHandle;
 
 import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -31,54 +32,58 @@
  * @hide
  */
 oneway interface IConnectionService {
-    void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
+    void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter,
+    in Session.Info sessionInfo);
 
-    void removeConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
+    void removeConnectionServiceAdapter(in IConnectionServiceAdapter adapter,
+    in Session.Info sessionInfo);
 
     void createConnection(
             in PhoneAccountHandle connectionManagerPhoneAccount,
             String callId,
             in ConnectionRequest request,
             boolean isIncoming,
-            boolean isUnknown);
+            boolean isUnknown,
+            in Session.Info sessionInfo);
 
-    void abort(String callId);
+    void abort(String callId, in Session.Info sessionInfo);
 
-    void answerVideo(String callId, int videoState);
+    void answerVideo(String callId, int videoState, in Session.Info sessionInfo);
 
-    void answer(String callId);
+    void answer(String callId, in Session.Info sessionInfo);
 
-    void reject(String callId);
+    void reject(String callId, in Session.Info sessionInfo);
 
-    void rejectWithMessage(String callId, String message);
+    void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
 
-    void disconnect(String callId);
+    void disconnect(String callId, in Session.Info sessionInfo);
 
-    void silence(String callId);
+    void silence(String callId, in Session.Info sessionInfo);
 
-    void hold(String callId);
+    void hold(String callId, in Session.Info sessionInfo);
 
-    void unhold(String callId);
+    void unhold(String callId, in Session.Info sessionInfo);
 
-    void onCallAudioStateChanged(String activeCallId, in CallAudioState callAudioState);
+    void onCallAudioStateChanged(String activeCallId, in CallAudioState callAudioState,
+    in Session.Info sessionInfo);
 
-    void playDtmfTone(String callId, char digit);
+    void playDtmfTone(String callId, char digit, in Session.Info sessionInfo);
 
-    void stopDtmfTone(String callId);
+    void stopDtmfTone(String callId, in Session.Info sessionInfo);
 
-    void conference(String conferenceCallId, String callId);
+    void conference(String conferenceCallId, String callId, in Session.Info sessionInfo);
 
-    void splitFromConference(String callId);
+    void splitFromConference(String callId, in Session.Info sessionInfo);
 
-    void mergeConference(String conferenceCallId);
+    void mergeConference(String conferenceCallId, in Session.Info sessionInfo);
 
-    void swapConference(String conferenceCallId);
+    void swapConference(String conferenceCallId, in Session.Info sessionInfo);
 
-    void onPostDialContinue(String callId, boolean proceed);
+    void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo);
 
-    void pullExternalCall(String callId);
+    void pullExternalCall(String callId, in Session.Info sessionInfo);
 
-    void sendCallEvent(String callId, String event, in Bundle extras);
+    void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
 
-    void onExtrasChanged(String callId, in Bundle extras);
+    void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9bc8ffe..35509d0 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
+import android.telecom.Logging.Session;
 import android.telecom.ParcelableConnection;
 import android.telecom.ParcelableConference;
 import android.telecom.StatusHints;
@@ -39,57 +40,66 @@
     void handleCreateConnectionComplete(
             String callId,
             in ConnectionRequest request,
-            in ParcelableConnection connection);
+            in ParcelableConnection connection,
+            in Session.Info sessionInfo);
 
-    void setActive(String callId);
+    void setActive(String callId, in Session.Info sessionInfo);
 
-    void setRinging(String callId);
+    void setRinging(String callId, in Session.Info sessionInfo);
 
-    void setDialing(String callId);
+    void setDialing(String callId, in Session.Info sessionInfo);
 
-    void setDisconnected(String callId, in DisconnectCause disconnectCause);
+    void setDisconnected(String callId, in DisconnectCause disconnectCause,
+    in Session.Info sessionInfo);
 
-    void setOnHold(String callId);
+    void setOnHold(String callId, in Session.Info sessionInfo);
 
-    void setRingbackRequested(String callId, boolean ringing);
+    void setRingbackRequested(String callId, boolean ringing, in Session.Info sessionInfo);
 
-    void setConnectionCapabilities(String callId, int connectionCapabilities);
+    void setConnectionCapabilities(String callId, int connectionCapabilities,
+    in Session.Info sessionInfo);
 
-    void setConnectionProperties(String callId, int connectionProperties);
+    void setConnectionProperties(String callId, int connectionProperties,
+    in Session.Info sessionInfo);
 
-    void setIsConferenced(String callId, String conferenceCallId);
+    void setIsConferenced(String callId, String conferenceCallId, in Session.Info sessionInfo);
 
-    void setConferenceMergeFailed(String callId);
+    void setConferenceMergeFailed(String callId, in Session.Info sessionInfo);
 
-    void addConferenceCall(String callId, in ParcelableConference conference);
+    void addConferenceCall(String callId, in ParcelableConference conference,
+    in Session.Info sessionInfo);
 
-    void removeCall(String callId);
+    void removeCall(String callId, in Session.Info sessionInfo);
 
-    void onPostDialWait(String callId, String remaining);
+    void onPostDialWait(String callId, String remaining, in Session.Info sessionInfo);
 
-    void onPostDialChar(String callId, char nextChar);
+    void onPostDialChar(String callId, char nextChar, in Session.Info sessionInfo);
 
-    void queryRemoteConnectionServices(RemoteServiceCallback callback);
+    void queryRemoteConnectionServices(RemoteServiceCallback callback, in Session.Info sessionInfo);
 
-    void setVideoProvider(String callId, IVideoProvider videoProvider);
+    void setVideoProvider(String callId, IVideoProvider videoProvider, in Session.Info sessionInfo);
 
-    void setVideoState(String callId, int videoState);
+    void setVideoState(String callId, int videoState, in Session.Info sessionInfo);
 
-    void setIsVoipAudioMode(String callId, boolean isVoip);
+    void setIsVoipAudioMode(String callId, boolean isVoip, in Session.Info sessionInfo);
 
-    void setStatusHints(String callId, in StatusHints statusHints);
+    void setStatusHints(String callId, in StatusHints statusHints, in Session.Info sessionInfo);
 
-    void setAddress(String callId, in Uri address, int presentation);
+    void setAddress(String callId, in Uri address, int presentation, in Session.Info sessionInfo);
 
-    void setCallerDisplayName(String callId, String callerDisplayName, int presentation);
+    void setCallerDisplayName(String callId, String callerDisplayName, int presentation,
+    in Session.Info sessionInfo);
 
-    void setConferenceableConnections(String callId, in List<String> conferenceableCallIds);
+    void setConferenceableConnections(String callId, in List<String> conferenceableCallIds,
+    in Session.Info sessionInfo);
 
-    void addExistingConnection(String callId, in ParcelableConnection connection);
+    void addExistingConnection(String callId, in ParcelableConnection connection,
+    in Session.Info sessionInfo);
 
-    void putExtras(String callId, in Bundle extras);
+    void putExtras(String callId, in Bundle extras, in Session.Info sessionInfo);
 
-    void removeExtras(String callId, in List<String> keys);
+    void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
 
-    void onConnectionEvent(String callId, String event, in Bundle extras);
+    void onConnectionEvent(String callId, String event, in Bundle extras,
+    in Session.Info sessionInfo);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 17f0da0..d064cd9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -262,6 +262,15 @@
     public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
 
     /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number.
+     * When empty string, no default voicemail number is specified.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+
+
+    /**
      * Flag specifying whether WFC over IMS should be available for carrier: independent of
      * carrier provisioning. If false: hard disabled. If true: then depends on carrier
      * provisioning, availability etc.
@@ -768,6 +777,7 @@
         sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
         sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 3c0a8d6..434caad 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -168,20 +168,34 @@
     }
 
     /**
-     * @hide
+     * Get reference signal received quality
      */
     public int getRsrq() {
         return mRsrq;
     }
 
     /**
-     * @hide
+     * Get reference signal signal-to-noise ratio
      */
     public int getRssnr() {
         return mRssnr;
     }
 
     /**
+     * Get reference signal received power
+     */
+    public int getRsrp() {
+        return mRsrp;
+    }
+
+    /**
+     * Get channel quality indicator
+     */
+    public int getCqi() {
+        return mCqi;
+    }
+
+    /**
      * Get signal strength as dBm
      */
     @Override
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index bb2b447..32f487b 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -233,7 +233,7 @@
      * @hide
      */
     /** @hide */
-    protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    protected Integer mSubId;
 
     private final Handler mHandler;
 
@@ -242,7 +242,7 @@
      * This class requires Looper.myLooper() not return null.
      */
     public PhoneStateListener() {
-        this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, Looper.myLooper());
+        this(null, Looper.myLooper());
     }
 
     /**
@@ -251,7 +251,7 @@
      * @hide
      */
     public PhoneStateListener(Looper looper) {
-        this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, looper);
+        this(null, looper);
     }
 
     /**
@@ -260,7 +260,7 @@
      * own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
      * @hide
      */
-    public PhoneStateListener(int subId) {
+    public PhoneStateListener(Integer subId) {
         this(subId, Looper.myLooper());
     }
 
@@ -269,7 +269,7 @@
      * and non-null Looper.
      * @hide
      */
-    public PhoneStateListener(int subId, Looper looper) {
+    public PhoneStateListener(Integer subId, Looper looper) {
         if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
         mSubId = subId;
         mHandler = new Handler(looper) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0875ff9..c6ed04c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -255,6 +255,22 @@
       return new TelephonyManager(mContext, subId);
     }
 
+    /**
+     * Create a new TelephonyManager object pinned to the subscription ID associated with the given
+     * phone account.
+     *
+     * @return a TelephonyManager that uses the given phone account for all calls, or {@code null}
+     * if the phone account does not correspond to a valid subscription ID.
+     */
+    @Nullable
+    public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+        int subId = getSubIdForPhoneAccountHandle(phoneAccountHandle);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return null;
+        }
+        return new TelephonyManager(mContext, subId);
+    }
+
     /** {@hide} */
     public boolean isMultiSimEnabled() {
         return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") ||
@@ -1118,7 +1134,7 @@
     private int getPhoneTypeFromProperty(int phoneId) {
         String type = getTelephonyProperty(phoneId,
                 TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
-        if (type == null || type.equals("")) {
+        if (type == null || type.isEmpty()) {
             return getPhoneTypeFromNetworkType(phoneId);
         }
         return Integer.parseInt(type);
@@ -1134,7 +1150,7 @@
         // use the system property for default network type.
         // This is a fail safe, and can only happen at first boot.
         String mode = getTelephonyProperty(phoneId, "ro.telephony.default_network", null);
-        if (mode != null) {
+        if (mode != null && !mode.isEmpty()) {
             return TelephonyManager.getPhoneType(Integer.parseInt(mode));
         }
         return TelephonyManager.PHONE_TYPE_NONE;
@@ -2783,6 +2799,12 @@
         if (mContext == null) return;
         try {
             Boolean notifyNow = (getITelephony() != null);
+            // If the listener has not explicitly set the subId (for example, created with the
+            // default constructor), replace the subId so it will listen to the account the
+            // telephony manager is created with.
+            if (listener.mSubId == null) {
+                listener.mSubId = mSubId;
+            }
             sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
                     listener.callback, events, notifyNow);
         } catch (RemoteException ex) {
@@ -2952,7 +2974,10 @@
      * methods may return true.
      *
      * <p>This method returns valid data for registered cells on devices with
-     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}. In cases where only
+     * partial information is available for a particular CellInfo entry, unavailable fields
+     * will be reported as Integer.MAX_VALUE. All reported cells will include at least a
+     * valid set of technology-specific identification info and a power level measurement.
      *
      *<p>
      * This method is preferred over using {@link
@@ -5143,6 +5168,19 @@
         return retval;
     }
 
+    private int getSubIdForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+        int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            ITelecomService service = getTelecomService();
+            if (service != null) {
+                retval = getSubIdForPhoneAccount(service.getPhoneAccount(phoneAccountHandle));
+            }
+        } catch (RemoteException e) {
+        }
+
+        return retval;
+    }
+
     /**
      * Resets telephony manager settings back to factory defaults.
      *
@@ -5192,6 +5230,16 @@
     }
 
     /**
+     * Returns the current {@link ServiceState} information.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     */
+    public ServiceState getServiceState() {
+        return getServiceStateForSubscriber(getSubId());
+    }
+
+    /**
      * Returns the service state information on specified subscription. Callers require
      * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
      * @hide
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 9939c18..cc26b4f 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -833,6 +833,7 @@
         bpp = 4;
     } else {
         printf("Unknown color type %d.\n", color_type);
+        return;
     }
 
     for (j = 0; j < h; j++) {
diff --git a/tools/aapt/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
index 54a8e9c..5339285 100644
--- a/tools/aapt/ZipEntry.cpp
+++ b/tools/aapt/ZipEntry.cpp
@@ -23,9 +23,10 @@
 #include "ZipEntry.h"
 #include <utils/Log.h>
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
-#include <assert.h>
+#include <time.h>
 
 using namespace android;
 
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index f5e49f1..042ff0e 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -72,8 +72,8 @@
             std::stringstream errorStr;
             errorStr << "unable to open file: " << strerror(errno);
             *outError = errorStr.str();
-            return {};
         }
+        return {};
     }
 
     std::vector<std::string> files;
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
index ce877b3..09d95ff 100644
--- a/tools/preload2/Android.mk
+++ b/tools/preload2/Android.mk
@@ -5,7 +5,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 
 # To connect to devices (and take hprof dumps).
-LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt tools-common-prebuilt
 
 # To process hprof dumps.
 LOCAL_STATIC_JAVA_LIBRARIES += perflib-prebuilt trove-prebuilt guavalib
diff --git a/tools/split-select/Android.mk b/tools/split-select/Android.mk
index 199fafa..4a1511e 100644
--- a/tools/split-select/Android.mk
+++ b/tools/split-select/Android.mk
@@ -73,7 +73,7 @@
 LOCAL_MODULE_HOST_OS := darwin linux windows
 
 LOCAL_SRC_FILES := $(sources)
-
+LOCAL_STATIC_LIBRARIES := $(hostStaticLibs)
 LOCAL_C_INCLUDES := $(cIncludes)
 LOCAL_CFLAGS := $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
 
diff --git a/wifi/java/android/net/wifi/EAPConstants.java b/wifi/java/android/net/wifi/EAPConstants.java
new file mode 100644
index 0000000..b5f7c94
--- /dev/null
+++ b/wifi/java/android/net/wifi/EAPConstants.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi;
+
+/**
+ * Utility class containing EAP (Extensible Authentication Protocol) Related constants.
+ *
+ * @hide
+ */
+public final class EAPConstants {
+    // Constant definition for EAP types. Refer to
+    // http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info.
+    public static final int EAP_MD5 = 4;
+    public static final int EAP_OTP = 5;
+    public static final int EAP_RSA = 9;
+    public static final int EAP_KEA = 11;
+    public static final int EAP_KEA_VALIDATE = 12;
+    public static final int EAP_TLS = 13;
+    public static final int EAP_LEAP = 17;
+    public static final int EAP_SIM = 18;
+    public static final int EAP_TTLS = 21;
+    public static final int EAP_AKA = 23;
+    public static final int EAP_3Com = 24;
+    public static final int EAP_MSCHAPv2 = 26;
+    public static final int EAP_PEAP = 29;
+    public static final int EAP_POTP = 32;
+    public static final int EAP_ActiontecWireless = 35;
+    public static final int EAP_HTTPDigest = 38;
+    public static final int EAP_SPEKE = 41;
+    public static final int EAP_MOBAC = 42;
+    public static final int EAP_FAST = 43;
+    public static final int EAP_ZLXEAP = 44;
+    public static final int EAP_Link = 45;
+    public static final int EAP_PAX = 46;
+    public static final int EAP_PSK = 47;
+    public static final int EAP_SAKE = 48;
+    public static final int EAP_IKEv2 = 49;
+    public static final int EAP_AKA_PRIME = 50;
+    public static final int EAP_GPSK = 51;
+    public static final int EAP_PWD = 52;
+    public static final int EAP_EKE = 53;
+    public static final int EAP_TEAP = 55;
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a91c949..9e897bf 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.ScanSettings;
@@ -58,10 +59,11 @@
 
     int addOrUpdateNetwork(in WifiConfiguration config);
 
-    int addPasspointManagementObject(String mo);
+    boolean addPasspointConfiguration(in PasspointConfiguration config);
 
-    int modifyPasspointManagementObject(String fqdn,
-                                        in List<PasspointManagementObjectDefinition> mos);
+    boolean removePasspointConfiguration(in String fqdn);
+
+    List<PasspointConfiguration> getPasspointConfigurations();
 
     void queryPasspointIcon(long bssid, String fileName);
 
@@ -89,7 +91,7 @@
 
     WifiInfo getConnectionInfo();
 
-    boolean setWifiEnabled(boolean enable);
+    boolean setWifiEnabled(String packageName, boolean enable);
 
     int getWifiEnabledState();
 
@@ -125,8 +127,6 @@
 
     WifiConfiguration getWifiApConfiguration();
 
-    WifiConfiguration buildWifiConfig(String uriString, String mimeType, in byte[] data);
-
     void setWifiApConfiguration(in WifiConfiguration wifiConfig);
 
     Messenger getWifiServiceMessenger();
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 465addf..da87135 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -66,6 +66,93 @@
      * supported by the access point.
      */
     public String capabilities;
+
+    /**
+     * @hide
+     * No security protocol.
+     */
+    public static final int PROTOCOL_NONE = 0;
+    /**
+     * @hide
+     * Security protocol type: WPA version 1.
+     */
+    public static final int PROTOCOL_WPA = 1;
+    /**
+     * @hide
+     * Security protocol type: WPA version 2, also called RSN.
+     */
+    public static final int PROTOCOL_WPA2 = 2;
+    /**
+     * @hide
+     * Security protocol type:
+     * OSU Server-only authenticated layer 2 Encryption Network.
+     * Used for Hotspot 2.0.
+     */
+    public static final int PROTOCOL_OSEN = 3;
+
+    /**
+     * @hide
+     * No security key management scheme.
+     */
+    public static final int KEY_MGMT_NONE = 0;
+    /**
+     * @hide
+     * Security key management scheme: PSK.
+     */
+    public static final int KEY_MGMT_PSK = 1;
+    /**
+     * @hide
+     * Security key management scheme: EAP.
+     */
+    public static final int KEY_MGMT_EAP = 2;
+    /**
+     * @hide
+     * Security key management scheme: FT_PSK.
+     */
+    public static final int KEY_MGMT_FT_PSK = 3;
+    /**
+     * @hide
+     * Security key management scheme: FT_EAP.
+     */
+    public static final int KEY_MGMT_FT_EAP = 4;
+    /**
+     * @hide
+     * Security key management scheme: PSK_SHA256
+     */
+    public static final int KEY_MGMT_PSK_SHA256 = 5;
+    /**
+     * @hide
+     * Security key management scheme: EAP_SHA256.
+     */
+    public static final int KEY_MGMT_EAP_SHA256 = 6;
+    /**
+     * @hide
+     * Security key management scheme: OSEN.
+     * Used for Hotspot 2.0.
+     */
+    public static final int KEY_MGMT_OSEN = 7;
+
+    /**
+     * @hide
+     * No cipher suite.
+     */
+    public static final int CIPHER_NONE = 0;
+    /**
+     * @hide
+     * No group addressed, only used for group data cipher.
+     */
+    public static final int CIPHER_NO_GROUP_ADDRESSED = 1;
+    /**
+     * @hide
+     * Cipher suite: TKIP
+     */
+    public static final int CIPHER_TKIP = 2;
+    /**
+     * @hide
+     * Cipher suite: CCMP
+     */
+    public static final int CIPHER_CCMP = 3;
+
     /**
      * The detected signal level in dBm, also known as the RSSI.
      *
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 8d5efba..e48f7bd 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -22,6 +22,7 @@
 import android.net.NetworkUtils;
 import android.text.TextUtils;
 
+import java.lang.Math;
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.UnknownHostException;
@@ -136,6 +137,15 @@
      */
     public double rxSuccessRate;
 
+    private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
+    private static final long FILTER_TIME_CONSTANT = 3000;
+    /**
+     * This factor is used to adjust the rate output under the new algorithm
+     * such that the result is comparable to the previous algorithm.
+     */
+    private static final long OUTPUT_SCALE_FACTOR = 5000;
+    private long mLastPacketCountUpdateTimeStamp;
+
     /**
      * @hide
      */
@@ -157,10 +167,9 @@
     public int score;
 
     /**
-     * TODO: get actual timestamp and calculate true rates
      * @hide
      */
-    public void updatePacketRates(WifiLinkLayerStats stats) {
+    public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
         if (stats != null) {
             long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
             long txretries = stats.retries_be + stats.retries_bk
@@ -169,18 +178,28 @@
             long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
                     + stats.lostmpdu_vi + stats.lostmpdu_vo;
 
-            if (txBad <= txbad
+            if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
+                    && mLastPacketCountUpdateTimeStamp < timeStamp
+                    && txBad <= txbad
                     && txSuccess <= txgood
                     && rxSuccess <= rxgood
                     && txRetries <= txretries) {
-                txBadRate = (txBadRate * 0.5)
-                        + ((double) (txbad - txBad) * 0.5);
-                txSuccessRate = (txSuccessRate * 0.5)
-                        + ((double) (txgood - txSuccess) * 0.5);
-                rxSuccessRate = (rxSuccessRate * 0.5)
-                        + ((double) (rxgood - rxSuccess) * 0.5);
-                txRetriesRate = (txRetriesRate * 0.5)
-                        + ((double) (txretries - txRetries) * 0.5);
+                    long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
+                    double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
+                    double currentSampleWeight = 1.0 - lastSampleWeight;
+
+                    txBadRate = txBadRate * lastSampleWeight
+                        + (txbad - txBad) * OUTPUT_SCALE_FACTOR / timeDelta
+                        * currentSampleWeight;
+                    txSuccessRate = txSuccessRate * lastSampleWeight
+                        + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+                        * currentSampleWeight;
+                    rxSuccessRate = rxSuccessRate * lastSampleWeight
+                        + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+                        * currentSampleWeight;
+                    txRetriesRate = txRetriesRate * lastSampleWeight
+                        + (txretries - txRetries) * OUTPUT_SCALE_FACTOR / timeDelta
+                        * currentSampleWeight;
             } else {
                 txBadRate = 0;
                 txSuccessRate = 0;
@@ -191,6 +210,7 @@
             txSuccess = txgood;
             rxSuccess = rxgood;
             txRetries = txretries;
+            mLastPacketCountUpdateTimeStamp = timeStamp;
         } else {
             txBad = 0;
             txSuccess = 0;
@@ -200,6 +220,7 @@
             txSuccessRate = 0;
             rxSuccessRate = 0;
             txRetriesRate = 0;
+            mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
         }
     }
 
@@ -243,6 +264,7 @@
         mRssi = INVALID_RSSI;
         mLinkSpeed = -1;
         mFrequency = -1;
+        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
     }
 
     /** @hide */
@@ -268,6 +290,7 @@
         badRssiCount = 0;
         linkStuckCount = 0;
         score = 0;
+        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
     }
 
     /**
@@ -295,6 +318,8 @@
             txRetriesRate = source.txRetriesRate;
             txSuccessRate = source.txSuccessRate;
             rxSuccessRate = source.rxSuccessRate;
+            mLastPacketCountUpdateTimeStamp =
+                source.mLastPacketCountUpdateTimeStamp;
             score = source.score;
             badRssiCount = source.badRssiCount;
             lowRssiCount = source.lowRssiCount;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d04a60e..a42bef4 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -19,12 +19,14 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -561,6 +563,28 @@
     public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
 
     /**
+     * Activity Action: Show UI to get user approval to enable WiFi.
+     * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+     *           the name of the app requesting the action.
+     * <p>Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE";
+
+    /**
+     * Activity Action: Show UI to get user approval to disable WiFi.
+     * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+     *           the name of the app requesting the action.
+     * <p>Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
+
+    /**
      * Internally used Wi-Fi lock mode representing the case were no locks are held.
      * @hide
      */
@@ -816,30 +840,50 @@
     }
 
     /**
-     * Add a Hotspot 2.0 release 2 Management Object
-     * @param mo The MO in XML form
-     * @return -1 for failure
+     * Add a Passpoint configuration.  The configuration provides a credential
+     * for connecting to Passpoint networks that are operated by the Passpoint
+     * service provider specified in the configuration.
+     *
+     * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain
+     * Name).  In the case when there is an existing configuration with the same base
+     * domain, the new configuration will replace the existing configuration.
+     *
+     * @param config The Passpoint configuration to be added
+     * @return true on success or false on failure
      * @hide
      */
-    public int addPasspointManagementObject(String mo) {
+    public boolean addPasspointConfiguration(PasspointConfiguration config) {
         try {
-            return mService.addPasspointManagementObject(mo);
+            return mService.addPasspointConfiguration(config);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Modify a Hotspot 2.0 release 2 Management Object
-     * @param fqdn The FQDN of the service provider
-     * @param mos A List of MO definitions to be updated
-     * @return the number of nodes updated, or -1 for failure
+     * Remove a Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+     *
+     * @param fqdn The FQDN of the passpoint configuration to be removed
+     * @return true on success or false on failure
      * @hide
      */
-    public int modifyPasspointManagementObject(String fqdn,
-                                               List<PasspointManagementObjectDefinition> mos) {
+    public boolean removePasspointConfiguration(String fqdn) {
         try {
-            return mService.modifyPasspointManagementObject(fqdn, mos);
+            return mService.removePasspointConfiguration(fqdn);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the list of installed Passpoint configurations.
+     *
+     * @return A list of PasspointConfiguration or null
+     * @hide
+     */
+    public List<PasspointConfiguration> getPasspointConfigurations() {
+        try {
+            return mService.getPasspointConfigurations();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1041,7 +1085,7 @@
     /** @hide */
     public static final int WIFI_FEATURE_SCANNER          = 0x0020;  // WifiScanner APIs
     /** @hide */
-    public static final int WIFI_FEATURE_NAN              = 0x0040;  // Neighbor Awareness Networking
+    public static final int WIFI_FEATURE_AWARE              = 0x0040;  // Wi-Fi AWare networking
     /** @hide */
     public static final int WIFI_FEATURE_D2D_RTT          = 0x0080;  // Device-to-device RTT
     /** @hide */
@@ -1122,8 +1166,8 @@
      * @return true if this adapter supports Neighbour Awareness Network APIs
      * @hide
      */
-    public boolean isNanSupported() {
-        return isFeatureSupported(WIFI_FEATURE_NAN);
+    public boolean isWifiAwareSupported() {
+        return isFeatureSupported(WIFI_FEATURE_AWARE);
     }
 
     /**
@@ -1409,7 +1453,7 @@
      */
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return mService.setWifiEnabled(enabled);
+            return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1552,20 +1596,6 @@
     }
 
     /**
-     * Builds a WifiConfiguration from Hotspot 2.0 MIME file.
-     * @return AP details in WifiConfiguration
-     *
-     * @hide Dont open yet
-     */
-    public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
-        try {
-            return mService.buildWifiConfig(uriString, mimeType, data);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Sets the Wi-Fi AP Configuration.
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl b/wifi/java/android/net/wifi/aware/ConfigRequest.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/ConfigRequest.aidl
rename to wifi/java/android/net/wifi/aware/ConfigRequest.aidl
index 38dddc2..68a7c85 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 parcelable ConfigRequest;
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
similarity index 96%
rename from wifi/java/android/net/wifi/nan/ConfigRequest.java
rename to wifi/java/android/net/wifi/aware/ConfigRequest.java
index bcd7932..4aacbae 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * Defines a request object to configure a Wi-Fi NAN network. Built using
+ * Defines a request object to configure a Wi-Fi Aware network. Built using
  * {@link ConfigRequest.Builder}. Configuration is requested using
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}.
+ * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}.
  * Note that the actual achieved configuration may be different from the
  * requested configuration - since different applications may request different
  * configurations.
@@ -221,7 +221,7 @@
         }
 
         /**
-         * The Cluster ID is generated randomly for new NAN networks. Specify
+         * The Cluster ID is generated randomly for new Aware networks. Specify
          * the lower range of the cluster ID. The upper range is specified using
          * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
          * range is 0 (the default) to the value specified by
@@ -246,7 +246,7 @@
         }
 
         /**
-         * The Cluster ID is generated randomly for new NAN networks. Specify
+         * The Cluster ID is generated randomly for new Aware networks. Specify
          * the lower upper of the cluster ID. The lower range is specified using
          * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
          * range is the value specified by
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
similarity index 88%
rename from wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index f2e371d..8ff3842 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 /**
- * Callback interface that WifiNanManager implements
+ * Callback interface that WifiAwareManager implements
  *
  * {@hide}
  */
-oneway interface IWifiNanDiscoverySessionCallback
+oneway interface IWifiAwareDiscoverySessionCallback
 {
     void onSessionStarted(int discoverySessionId);
     void onSessionConfigSuccess();
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
similarity index 85%
rename from wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
index 9ac7bf2..30dd64d 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
-import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.aware.ConfigRequest;
 import android.net.wifi.RttManager;
 
 /**
- * Callback interface that WifiNanManager implements
+ * Callback interface that WifiAwareManager implements
  *
  * {@hide}
  */
-oneway interface IWifiNanEventCallback
+oneway interface IWifiAwareEventCallback
 {
     void onConnectSuccess(int clientId);
     void onConnectFail(int reason);
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
similarity index 71%
rename from wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 5485824..9c92807 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -14,40 +14,40 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.app.PendingIntent;
 
-import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanDiscoverySessionCallback;
-import android.net.wifi.nan.IWifiNanEventCallback;
-import android.net.wifi.nan.PublishConfig;
-import android.net.wifi.nan.SubscribeConfig;
-import android.net.wifi.nan.WifiNanCharacteristics;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.WifiAwareCharacteristics;
 import android.net.wifi.RttManager;
 
 /**
- * Interface that WifiNanService implements
+ * Interface that WifiAwareService implements
  *
  * {@hide}
  */
-interface IWifiNanManager
+interface IWifiAwareManager
 {
-    // NAN API
+    // Aware API
     void enableUsage();
     void disableUsage();
     boolean isUsageEnabled();
-    WifiNanCharacteristics getCharacteristics();
+    WifiAwareCharacteristics getCharacteristics();
 
     // client API
-    void connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback,
+    void connect(in IBinder binder, in String callingPackage, in IWifiAwareEventCallback callback,
             in ConfigRequest configRequest, boolean notifyOnIdentityChanged);
     void disconnect(int clientId, in IBinder binder);
 
     void publish(int clientId, in PublishConfig publishConfig,
-            in IWifiNanDiscoverySessionCallback callback);
+            in IWifiAwareDiscoverySessionCallback callback);
     void subscribe(int clientId, in SubscribeConfig subscribeConfig,
-            in IWifiNanDiscoverySessionCallback callback);
+            in IWifiAwareDiscoverySessionCallback callback);
 
     // session API
     void updatePublish(int clientId, int discoverySessionId, in PublishConfig publishConfig);
diff --git a/wifi/java/android/net/wifi/nan/LvBufferUtils.java b/wifi/java/android/net/wifi/aware/LvBufferUtils.java
similarity index 99%
rename from wifi/java/android/net/wifi/nan/LvBufferUtils.java
rename to wifi/java/android/net/wifi/aware/LvBufferUtils.java
index eb56070..3265243 100644
--- a/wifi/java/android/net/wifi/nan/LvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/LvBufferUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.Nullable;
 
@@ -28,7 +28,7 @@
  * Length/Value format. The utilities accept a configuration of the size of
  * the Length field.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
 public class LvBufferUtils {
     private LvBufferUtils() {
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/aware/PublishConfig.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/PublishConfig.aidl
rename to wifi/java/android/net/wifi/aware/PublishConfig.aidl
index 5f66d16..2e6dd00 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 parcelable PublishConfig;
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
similarity index 92%
rename from wifi/java/android/net/wifi/nan/PublishConfig.java
rename to wifi/java/android/net/wifi/aware/PublishConfig.java
index 30c5bc0..5d3ad058 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -30,13 +30,14 @@
 import java.util.Arrays;
 
 /**
- * Defines the configuration of a NAN publish session. Built using
+ * Defines the configuration of a Aware publish session. Built using
  * {@link PublishConfig.Builder}. A publish session is created using
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
+ * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig,
+ * WifiAwareDiscoverySessionCallback)}
  * or updated using
- * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}.
+ * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
 public final class PublishConfig implements Parcelable {
     /** @hide */
@@ -182,8 +183,9 @@
      *
      * @hide
      */
-    public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
-        WifiNanUtils.validateServiceName(mServiceName);
+    public void assertValid(WifiAwareCharacteristics characteristics)
+            throws IllegalArgumentException {
+        WifiAwareUtils.validateServiceName(mServiceName);
 
         if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
             throw new IllegalArgumentException(
@@ -320,12 +322,12 @@
          * Sets the number of times an unsolicited (configured using
          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
          * will be broadcast. When the count is reached an event will be
-         * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}
-         * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
+         * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
+         * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
          * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
          *     called.
          *
          * @param publishCount Number of publish packets to broadcast.
@@ -346,12 +348,12 @@
          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
          * will be alive - broadcasting a packet. When the TTL is reached
          * an event will be generated for
-         * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with
-         * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}  [unless
+         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
+         * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}  [unless
          * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
          *     called.
          *
          * @param ttlSec Lifetime of a publish session in seconds.
@@ -369,7 +371,7 @@
 
         /**
          * Configure whether a publish terminate notification
-         * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported
+         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl b/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
rename to wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
index 92344a4..bd73d5e 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 parcelable SubscribeConfig;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
similarity index 93%
rename from wifi/java/android/net/wifi/nan/SubscribeConfig.java
rename to wifi/java/android/net/wifi/aware/SubscribeConfig.java
index ea7b8e4..2a6cc93 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -30,13 +30,14 @@
 import java.util.Arrays;
 
 /**
- * Defines the configuration of a NAN subscribe session. Built using
+ * Defines the configuration of a Aware subscribe session. Built using
  * {@link SubscribeConfig.Builder}. Subscribe is done using
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
+ * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig,
+ * WifiAwareDiscoverySessionCallback)}
  * or
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
 public final class SubscribeConfig implements Parcelable {
     /** @hide */
@@ -209,8 +210,9 @@
      *
      * @hide
      */
-    public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
-        WifiNanUtils.validateServiceName(mServiceName);
+    public void assertValid(WifiAwareCharacteristics characteristics)
+            throws IllegalArgumentException {
+        WifiAwareUtils.validateServiceName(mServiceName);
 
         if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
             throw new IllegalArgumentException(
@@ -352,11 +354,11 @@
          * Sets the number of times an active (
          * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
          * will broadcast. When the count is reached an event will be
-         * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}
-         * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+         * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
+         * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
          *     called.
          *
          * @param subscribeCount Number of subscribe packets to broadcast.
@@ -377,11 +379,11 @@
          * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
          * will be alive - i.e. broadcasting a packet. When the TTL is reached
          * an event will be generated for
-         * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with
-         * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
+         * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
          * <p>
          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
-         *     Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+         *     Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
          *     called.
          *
          * @param ttlSec Lifetime of a subscribe session in seconds.
@@ -401,7 +403,7 @@
          * Sets the match style of the subscription - how are matches from a
          * single match session (corresponding to the same publish action on the
          * peer) reported to the host (using the
-         * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
+         * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
          * ). The options are: only report the first match and ignore the rest
          * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single
          * match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default).
@@ -421,7 +423,7 @@
 
         /**
          * Configure whether a subscribe terminate notification
-         * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported
+         * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
similarity index 99%
rename from wifi/java/android/net/wifi/nan/TlvBufferUtils.java
rename to wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index 2c5aab4..56c9069 100644
--- a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.Nullable;
 
@@ -32,7 +32,7 @@
  * the Type field and the Length field. A Type field size of 0 is allowed -
  * allowing usage for LV (no T) array formats.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
 public class TlvBufferUtils {
     private TlvBufferUtils() {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
new file mode 100644
index 0000000..2cace61
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.aware;
+
+/**
+ * Base class for Aware attach callbacks. Should be extended by applications and set when calling
+ * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}. These are callbacks
+ * applying to the Aware connection as a whole - not to specific publish or subscribe sessions -
+ * for that see {@link WifiAwareDiscoverySessionCallback}.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class WifiAwareAttachCallback {
+    /**
+     * Called when Aware attach operation
+     * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}
+     * is completed and that we can now start discovery sessions or connections.
+     *
+     * @param session The Aware object on which we can execute further Aware operations - e.g.
+     *                discovery, connections.
+     */
+    public void onAttached(WifiAwareSession session) {
+        /* empty */
+    }
+
+    /**
+     * Called when Aware attach operation
+     * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)} failed.
+     */
+    public void onAttachFailed() {
+        /* empty */
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
similarity index 89%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
index 5f66d16..a35e71d 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
-parcelable PublishConfig;
+parcelable WifiAwareCharacteristics;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
similarity index 73%
rename from wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
rename to wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
index f43ed4d..6232c14 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * The characteristics of the Wi-Fi NAN implementation.
+ * The characteristics of the Wi-Fi Aware implementation.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanCharacteristics implements Parcelable {
+public class WifiAwareCharacteristics implements Parcelable {
     /** @hide */
     public static final String KEY_MAX_SERVICE_NAME_LENGTH = "key_max_service_name_length";
     /** @hide */
@@ -37,41 +37,41 @@
     private Bundle mCharacteristics = new Bundle();
 
     /** @hide : should not be created by apps */
-    public WifiNanCharacteristics(Bundle characteristics) {
+    public WifiAwareCharacteristics(Bundle characteristics) {
         mCharacteristics = characteristics;
     }
 
     /**
-     * Returns the maximum string length that can be used to specify a NAN service name. Restricts
+     * Returns the maximum string length that can be used to specify a Aware service name. Restricts
      * the parameters of the {@link PublishConfig.Builder#setServiceName(String)} and
      * {@link SubscribeConfig.Builder#setServiceName(String)}.
      *
-     * @return A positive integer, maximum string length of NAN service name.
+     * @return A positive integer, maximum string length of Aware service name.
      */
     public int getMaxServiceNameLength() {
         return mCharacteristics.getInt(KEY_MAX_SERVICE_NAME_LENGTH);
     }
 
     /**
-     * Returns the maximum length of byte array that can be used to specify a NAN service specific
-     * information field: the arbitrary load used in discovery or the message length of NAN
+     * Returns the maximum length of byte array that can be used to specify a Aware service specific
+     * information field: the arbitrary load used in discovery or the message length of Aware
      * message exchange. Restricts the parameters of the
      * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])},
      * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, and
-     * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} variants.
+     * {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} variants.
      *
-     * @return A positive integer, maximum length of byte array for NAN messaging.
+     * @return A positive integer, maximum length of byte array for Aware messaging.
      */
     public int getMaxServiceSpecificInfoLength() {
         return mCharacteristics.getInt(KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH);
     }
 
     /**
-     * Returns the maximum length of byte array that can be used to specify a NAN match filter.
+     * Returns the maximum length of byte array that can be used to specify a Aware match filter.
      * Restricts the parameters of the {@link PublishConfig.Builder#setMatchFilter(byte[])} and
      * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}.
      *
-     * @return A positive integer, maximum legngth of byte array for NAN discovery match filter.
+     * @return A positive integer, maximum legngth of byte array for Aware discovery match filter.
      */
     public int getMaxMatchFilterLength() {
         return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH);
@@ -87,17 +87,17 @@
         return 0;
     }
 
-    public static final Creator<WifiNanCharacteristics> CREATOR =
-            new Creator<WifiNanCharacteristics>() {
+    public static final Creator<WifiAwareCharacteristics> CREATOR =
+            new Creator<WifiAwareCharacteristics>() {
                 @Override
-                public WifiNanCharacteristics createFromParcel(Parcel in) {
-                    WifiNanCharacteristics c = new WifiNanCharacteristics(in.readBundle());
+                public WifiAwareCharacteristics createFromParcel(Parcel in) {
+                    WifiAwareCharacteristics c = new WifiAwareCharacteristics(in.readBundle());
                     return c;
                 }
 
                 @Override
-                public WifiNanCharacteristics[] newArray(int size) {
-                    return new WifiNanCharacteristics[size];
+                public WifiAwareCharacteristics[] newArray(int size) {
+                    return new WifiAwareCharacteristics[size];
                 }
             };
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
similarity index 70%
rename from wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java
rename to wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
index 17e974b..07f7523 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,30 +27,30 @@
 import java.lang.ref.WeakReference;
 
 /**
- * A class representing a single publish or subscribe NAN session. This object
+ * A class representing a single publish or subscribe Aware session. This object
  * will not be created directly - only its child classes are available:
- * {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}. This
+ * {@link WifiAwarePublishDiscoverySession} and {@link WifiAwareSubscribeDiscoverySession}. This
  * class provides functionality common to both publish and subscribe discovery sessions:
  * <ul>
  *     <li>Sending messages: {@link #sendMessage(Object, int, byte[])} or
  *     {@link #sendMessage(Object, int, byte[], int)} methods.
- *     <li>Creating a network-specifier when requesting a NAN connection:
+ *     <li>Creating a network-specifier when requesting a Aware connection:
  *     {@link #createNetworkSpecifier(int, Object, byte[])}.
  * </ul>
  * The {@link #destroy()} method must be called to destroy discovery sessions once they are
  * no longer needed.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanDiscoveryBaseSession {
-    private static final String TAG = "WifiNanDiscoveryBaseSsn";
+public class WifiAwareDiscoveryBaseSession {
+    private static final String TAG = "WifiAwareDiscBaseSsn";
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     private static final int MAX_SEND_RETRY_COUNT = 5;
 
     /** @hide */
-    protected WeakReference<WifiNanManager> mMgr;
+    protected WeakReference<WifiAwareManager> mMgr;
     /** @hide */
     protected final int mClientId;
     /** @hide */
@@ -71,7 +71,7 @@
     }
 
     /** @hide */
-    public WifiNanDiscoveryBaseSession(WifiNanManager manager, int clientId, int sessionId) {
+    public WifiAwareDiscoveryBaseSession(WifiAwareManager manager, int clientId, int sessionId) {
         if (VDBG) {
             Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId="
                     + clientId + ", sessionId=" + sessionId);
@@ -93,12 +93,12 @@
      *     This operation must be done on a session which is no longer needed. Otherwise system
      *     resources will continue to be utilized until the application exits. The only
      *     exception is a session for which we received a termination callback,
-     *     {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}.
+     *     {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}.
      */
     public void destroy() {
-        WifiNanManager mgr = mMgr.get();
+        WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
-            Log.w(TAG, "destroy: called post GC on WifiNanManager");
+            Log.w(TAG, "destroy: called post GC on WifiAwareManager");
             return;
         }
         mgr.terminateSession(mClientId, mSessionId);
@@ -137,26 +137,26 @@
     }
 
     /**
-     * Sends a message to the specified destination. NAN messages are transmitted in the context
+     * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
-     * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
+     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
      * <p>
-     *     NAN messages are not guaranteed delivery. Callbacks on
-     *     {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully,
-     *     {@link WifiNanDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
+     *     Aware messages are not guaranteed delivery. Callbacks on
+     *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
      *     (possibly after several retries) -
-     *     {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)}.
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
      * <p>
      *     The peer will get a callback indicating a message was received using
-     *     {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
-     *        {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
+     *        {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
      *        or
-     *        {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
+     *        {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicating message send success or
-     *            failure. The {@code messageId} is not used internally by the NAN service - it
+     *            failure. The {@code messageId} is not used internally by the Aware service - it
      *                  can be arbitrary and non-unique.
      * @param message The message to be transmitted.
      * @param retryCount An integer specifying how many additional service-level (as opposed to PHY
@@ -170,9 +170,9 @@
             Log.w(TAG, "sendMessage: called on terminated session");
             return;
         } else {
-            WifiNanManager mgr = mMgr.get();
+            WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "sendMessage: called post GC on WifiNanManager");
+                Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
                 return;
             }
 
@@ -181,28 +181,28 @@
     }
 
     /**
-     * Sends a message to the specified destination. NAN messages are transmitted in the context
+     * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
-     * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
+     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
      * <p>
-     *     NAN messages are not guaranteed delivery. Callbacks on
-     *     {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully,
-     *     {@link WifiNanDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
+     *     Aware messages are not guaranteed delivery. Callbacks on
+     *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
      *     (possibly after several retries) -
-     *     {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)}.
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
      * <p>
      *     The peer will get a callback indicating a message was received using
-     *     {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
+     *     {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
      * Equivalent to {@link #sendMessage(Object, int, byte[], int)} with a {@code retryCount} of
      * 0.
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
-     *        {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
+     *        {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
      *        or
-     *        {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
+     *        {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicating message send success or
-     *            failure. The {@code messageId} is not used internally by the NAN service - it
+     *            failure. The {@code messageId} is not used internally by the Aware service - it
      *                  can be arbitrary and non-unique.
      * @param message The message to be transmitted.
      */
@@ -212,8 +212,8 @@
 
     /**
      * Start a ranging operation with the specified peers. The peer IDs are obtained from an
-     * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
-     * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} operation - can
+     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
+     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} operation - can
      * only range devices which are part of an ongoing discovery session.
      *
      * @param params   RTT parameters - each corresponding to a specific peer ID (the array sizes
@@ -221,16 +221,17 @@
      *                 {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
      *                 a peer ID - not to a MAC address.
      * @param listener The listener to receive the results of the ranging session.
-     * @hide PROPOSED_NAN_SYSTEM_API [TODO: b/28847998 - track RTT API & visilibity]
+     * @hide PROPOSED_AWARE_SYSTEM_API
+     * [TODO: b/28847998 - track RTT API & visilibity]
      */
     public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
         if (mTerminated) {
             Log.w(TAG, "startRanging: called on terminated session");
             return;
         } else {
-            WifiNanManager mgr = mMgr.get();
+            WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "startRanging: called post GC on WifiNanManager");
+                Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
                 return;
             }
 
@@ -240,23 +241,23 @@
 
     /**
      * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
-     * WiFi NAN connection to the specified peer. The
+     * WiFi Aware connection to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
-     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
-     * This method should be used when setting up a connection with a peer discovered through NAN
+     * This method should be used when setting up a connection with a peer discovered through Aware
      * discovery or communication (in such scenarios the MAC address of the peer is shielded by
-     * an opaque peer ID handle). If a NAN connection is needed to a peer discovered using other
+     * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
      * OOB (out-of-band) mechanism then use the alternative
-     * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
+     * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
      * peer's MAC address.
      *
      * @param role The role of this device:
-     * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
-     * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
+     * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+     * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peerHandle The peer's handle obtained through
-     * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
-     * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}. On a RESPONDER
+     * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
+     * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])}. On a RESPONDER
      *               this value is used to gate the acceptance of a connection request from only
      *               that peer. A RESPONDER may specified a null - indicating that it will accept
      *               connection requests from any device.
@@ -268,18 +269,19 @@
      *
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
-     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,android.net.ConnectivityManager.NetworkCallback)}
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role,
+    public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
             @Nullable Object peerHandle, @Nullable byte[] token) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifier: called on terminated session");
             return null;
         } else {
-            WifiNanManager mgr = mMgr.get();
+            WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "createNetworkSpecifier: called post GC on WifiNanManager");
+                Log.w(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
                 return null;
             }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
similarity index 66%
rename from wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java
rename to wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
index 271f420..9dfa24f 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -23,18 +23,20 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Base class for NAN session events callbacks. Should be extended by
+ * Base class for Aware session events callbacks. Should be extended by
  * applications wanting notifications. The callbacks are set when a
  * publish or subscribe session is created using
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
+ * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig,
+ * WifiAwareDiscoverySessionCallback)}
  * or
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)} .
+ * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig,
+ * WifiAwareDiscoverySessionCallback)} .
  * <p>
  * A single callback is set at session creation - it cannot be replaced.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanDiscoverySessionCallback {
+public class WifiAwareDiscoverySessionCallback {
     /** @hide */
     @IntDef({
             TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
@@ -46,7 +48,7 @@
      * Indicates that publish or subscribe session is done - all the
      * requested operations (per {@link PublishConfig} or
      * {@link SubscribeConfig}) have been executed. Failure reason flag for
-     * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback.
+     * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
      */
     public static final int TERMINATE_REASON_DONE = 100;
 
@@ -54,39 +56,41 @@
      * Indicates that publish or subscribe session is terminated due to a
      * failure.
      * Failure reason flag for
-     * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback.
+     * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
      */
     public static final int TERMINATE_REASON_FAIL = 101;
 
     /**
      * Called when a publish operation is started successfully in response to a
-     * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
+     * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig,
+     * WifiAwareDiscoverySessionCallback)}
      * operation.
      *
-     * @param session The {@link WifiNanPublishDiscoverySession} used to control the
+     * @param session The {@link WifiAwarePublishDiscoverySession} used to control the
      *            discovery session.
      */
-    public void onPublishStarted(@NonNull WifiNanPublishDiscoverySession session) {
+    public void onPublishStarted(@NonNull WifiAwarePublishDiscoverySession session) {
         /* empty */
     }
 
     /**
      * Called when a subscribe operation is started successfully in response to a
-     * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
+     * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig,
+     * WifiAwareDiscoverySessionCallback)}
      * operation.
      *
-     * @param session The {@link WifiNanSubscribeDiscoverySession} used to control the
+     * @param session The {@link WifiAwareSubscribeDiscoverySession} used to control the
      *            discovery session.
      */
-    public void onSubscribeStarted(@NonNull WifiNanSubscribeDiscoverySession session) {
+    public void onSubscribeStarted(@NonNull WifiAwareSubscribeDiscoverySession session) {
         /* empty */
     }
 
     /**
      * Called when a publish or subscribe discovery session configuration update request
      * succeeds. Called in response to
-     * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or
-     * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
+     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
      */
     public void onSessionConfigUpdated() {
         /* empty */
@@ -94,12 +98,14 @@
 
     /**
      * Called when a publish or subscribe discovery session cannot be created:
-     * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
+     * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig,
+     * WifiAwareDiscoverySessionCallback)}
      * or
-     * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)},
+     * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig,
+     * WifiAwareDiscoverySessionCallback)},
      * or when a configuration update fails:
-     * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or
-     * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
+     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
      * <p>
      *     For discovery session updates failure leaves the session running with its previous
      *     configuration - the discovery session is not terminated.
@@ -110,12 +116,12 @@
 
     /**
      * Called when a discovery session (publish or subscribe) terminates. Termination may be due
-     * to user-request (either directly through {@link WifiNanDiscoveryBaseSession#destroy()} or
+     * to user-request (either directly through {@link WifiAwareDiscoveryBaseSession#destroy()} or
      * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
      * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure.
      *
      * @param reason The termination reason using
-     *            {@code WifiNanDiscoverySessionCallback.TERMINATE_*} codes.
+     *            {@code WifiAwareDiscoverySessionCallback.TERMINATE_*} codes.
      */
     public void onSessionTerminated(@SessionTerminateCodes int reason) {
         /* empty */
@@ -138,12 +144,12 @@
     }
 
     /**
-     * Called in response to {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])}
+     * Called in response to {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])}
      * when a message is transmitted successfully - i.e. when it was received successfully by the
      * peer (corresponds to an ACK being received).
      * <p>
      * Note that either this callback or
-     * {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)} will be
+     * {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)} will be
      * received - never both.
      *
      * @param messageId The arbitrary message ID specified when sending the message.
@@ -154,12 +160,12 @@
 
     /**
      * Called when message transmission fails - when no ACK is received from the peer.
-     * Retries when ACKs are not received are done by hardware, MAC, and in the NAN stack (using
-     * the {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[], int)} method) - this
-     * event is received after all retries are exhausted.
+     * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
+     * the {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[], int)} method) -
+     * this event is received after all retries are exhausted.
      * <p>
      * Note that either this callback or
-     * {@link WifiNanDiscoverySessionCallback#onMessageSent(int)} will be received
+     * {@link WifiAwareDiscoverySessionCallback#onMessageSent(int)} will be received
      * - never both.
      *
      * @param messageId The arbitrary message ID specified when sending the message.
@@ -170,8 +176,8 @@
 
     /**
      * Called when a message is received from a discovery session peer - in response to the
-     * peer's {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} or
-     * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[], int)}.
+     * peer's {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} or
+     * {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[], int)}.
      *
      * @param peerHandle An opaque handle to the peer matching our discovery operation.
      * @param message A byte array containing the message.
diff --git a/wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java b/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
similarity index 75%
rename from wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java
rename to wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
index 7cb928f..e8f52cd4 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
@@ -14,23 +14,23 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 /**
- * Base class for a listener which is called with the MAC address of the NAN interface whenever
+ * Base class for a listener which is called with the MAC address of the Aware interface whenever
  * it is changed. Change may be due to device joining a cluster, starting a cluster, or discovery
  * interface change (addresses are randomized at regular intervals). The implication is that
  * peers you've been communicating with may no longer recognize you and you need to re-establish
  * your identity - e.g. by starting a discovery session. This actual MAC address of the
- * interface may also be useful if the application uses alternative (non-NAN) discovery but needs
- * to set up a NAN connection. The provided NAN discovery interface MAC address can then be used
- * in {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * interface may also be useful if the application uses alternative (non-Aware) discovery but needs
+ * to set up a Aware connection. The provided Aware discovery interface MAC address can then be used
+ * in {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanIdentityChangedListener {
+public class WifiAwareIdentityChangedListener {
     /**
-     * @param mac The MAC address of the NAN discovery interface. The application must have the
+     * @param mac The MAC address of the Aware discovery interface. The application must have the
      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to get the actual MAC address,
      *            otherwise all 0's will be provided.
      */
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
similarity index 76%
rename from wifi/java/android/net/wifi/nan/WifiNanManager.java
rename to wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 002b953..10b70ab 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -48,80 +48,86 @@
 import java.util.Arrays;
 
 /**
- * This class provides the primary API for managing Wi-Fi NAN operations:
+ * This class provides the primary API for managing Wi-Fi Aware operations:
  * discovery and peer-to-peer data connections. Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(String)
- * Context.getSystemService(Context.WIFI_NAN_SERVICE)}.
+ * Context.getSystemService(Context.WIFI_AWARE_SERVICE)}.
  * <p>
  * The class provides access to:
  * <ul>
- * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to
- * {@link #attach(Handler, WifiNanAttachCallback)}.
+ * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to
+ * {@link #attach(Handler, WifiAwareAttachCallback)}.
  * <li>Create discovery sessions (publish or subscribe sessions). Refer to
- * {@link WifiNanSession#publish(Handler, PublishConfig, WifiNanDiscoverySessionCallback)} and
- * {@link WifiNanSession#subscribe(Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}.
- * <li>Create a NAN network specifier to be used with
+ * {@link WifiAwareSession#publish(Handler, PublishConfig, WifiAwareDiscoverySessionCallback)} and
+ * {@link WifiAwareSession#subscribe(Handler, SubscribeConfig, WifiAwareDiscoverySessionCallback)}.
+ * <li>Create a Aware network specifier to be used with
  * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
- * to set-up a NAN connection with a peer. Refer to
- * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])} and
- * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * to set-up a Aware connection with a peer. Refer to
+ * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])} and
+ * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
  * </ul>
  * <p>
- *     NAN may not be usable when Wi-Fi is disabled (and other conditions). To validate that
+ *     Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that
  *     the functionality is available use the {@link #isAvailable()} function. To track
- *     changes in NAN usability register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
- *     Note that this broadcast is not sticky - you should register for it and then check the
- *     above API to avoid a race condition.
+ *     changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
+ *     broadcast. Note that this broadcast is not sticky - you should register for it and then
+ *     check the above API to avoid a race condition.
  * <p>
- *     An application must use {@link #attach(Handler, WifiNanAttachCallback)} to initialize a NAN
- *     cluster - before making any other NAN operation. NAN cluster membership is a device-wide
- *     operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or
- *     starts one if none can be found). Information about attach success (or failure) are
- *     returned in callbacks of {@link WifiNanAttachCallback}. Proceed with NAN discovery or
- *     connection setup only after receiving confirmation that NAN attach succeeded -
- *     {@link WifiNanAttachCallback#onAttached(WifiNanSession)}. When an application is
- *     finished using NAN it <b>must</b> use the {@link WifiNanSession#destroy()} API
- *     to indicate to the NAN service that the device may detach from the NAN cluster. The
- *     device will actually disable NAN once the last application detaches.
+ *     An application must use {@link #attach(Handler, WifiAwareAttachCallback)} to initialize a
+ *     Aware cluster - before making any other Aware operation. Aware cluster membership is a
+ *     device-wide operation - the API guarantees that the device is in a cluster or joins a
+ *     Aware cluster (or starts one if none can be found). Information about attach success (or
+ *     failure) are returned in callbacks of {@link WifiAwareAttachCallback}. Proceed with Aware
+ *     discovery or connection setup only after receiving confirmation that Aware attach
+ *     succeeded - {@link WifiAwareAttachCallback#onAttached(WifiAwareSession)}. When an
+ *     application is finished using Aware it <b>must</b> use the
+ *     {@link WifiAwareSession#destroy()} API to indicate to the Aware service that the device
+ *     may detach from the Aware cluster. The device will actually disable Aware once the last
+ *     application detaches.
  * <p>
- *     Once a NAN attach is confirmed use the
- *     {@link WifiNanSession#publish(Handler, PublishConfig, WifiNanDiscoverySessionCallback)} or
- *     {@link WifiNanSession#subscribe(Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
- *     to create publish or subscribe NAN discovery sessions. Events are called on the provided
- *     callback object {@link WifiNanDiscoverySessionCallback}. Specifically, the
- *     {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}
+ *     Once a Aware attach is confirmed use the
+ *     {@link WifiAwareSession#publish(Handler, PublishConfig, WifiAwareDiscoverySessionCallback)}
+ *     or
+ *     {@link WifiAwareSession#subscribe(Handler, SubscribeConfig,
+ *     WifiAwareDiscoverySessionCallback)}
+ *     to create publish or subscribe Aware discovery sessions. Events are called on the provided
+ *     callback object {@link WifiAwareDiscoverySessionCallback}. Specifically, the
+ *     {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}
  *     and
- *     {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}
- *     return {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}
+ *     {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
+ *     WifiAwareSubscribeDiscoverySession)}
+ *     return {@link WifiAwarePublishDiscoverySession} and
+ *     {@link WifiAwareSubscribeDiscoverySession}
  *     objects respectively on which additional session operations can be performed, e.g. updating
- *     the session {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} and
- *     {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can also
- *     be used to send messages using the
- *     {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} APIs. When an
+ *     the session {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} and
+ *     {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can
+ *     also be used to send messages using the
+ *     {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} APIs. When an
  *     application is finished with a discovery session it <b>must</b> terminate it using the
- *     {@link WifiNanDiscoveryBaseSession#destroy()} API.
+ *     {@link WifiAwareDiscoveryBaseSession#destroy()} API.
  * <p>
- *    Creating connections between NAN devices is managed by the standard
- *    {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}.
+ *    Creating connections between Aware devices is managed by the standard
+ *    {@link ConnectivityManager#requestNetwork(NetworkRequest,
+ *    ConnectivityManager.NetworkCallback)}.
  *    The {@link NetworkRequest} object should be constructed with:
  *    <ul>
  *        <li>{@link NetworkRequest.Builder#addTransportType(int)} of
- *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+ *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
  *        <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
- *        {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} or
- *        {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
+ *        {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} or
+ *        {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
  *    </ul>
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanManager {
-    private static final String TAG = "WifiNanManager";
+public class WifiAwareManager {
+    private static final String TAG = "WifiAwareManager";
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
     /**
-     * Keys used to generate a Network Specifier for the NAN network request. The network specifier
-     * is formatted as a JSON string.
+     * Keys used to generate a Network Specifier for the Aware network request. The network
+     * specifier is formatted as a JSON string.
      */
 
     /**
@@ -197,44 +203,44 @@
     public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token";
 
     /**
-     * Broadcast intent action to indicate that the state of Wi-Fi NAN availability has changed.
+     * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
      * Use the {@link #isAvailable()} to query the current status.
      * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
-     * the broadcast to check the current state of Wi-Fi NAN.
+     * the broadcast to check the current state of Wi-Fi Aware.
      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
      * components will be launched.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_WIFI_NAN_STATE_CHANGED =
-            "android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED";
+    public static final String ACTION_WIFI_AWARE_STATE_CHANGED =
+            "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
 
     /** @hide */
     @IntDef({
-            WIFI_NAN_DATA_PATH_ROLE_INITIATOR, WIFI_NAN_DATA_PATH_ROLE_RESPONDER})
+            WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface DataPathRole {
     }
 
     /**
      * Connection creation role is that of INITIATOR. Used to create a network specifier string
-     * when requesting a NAN network.
+     * when requesting a Aware network.
      *
-     * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
-     * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[])
+     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
+     * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
-    public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0;
+    public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
 
     /**
      * Connection creation role is that of RESPONDER. Used to create a network specifier string
-     * when requesting a NAN network.
+     * when requesting a Aware network.
      *
-     * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
-     * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[])
+     * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
+     * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
      */
-    public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1;
+    public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
 
     private final Context mContext;
-    private final IWifiNanManager mService;
+    private final IWifiAwareManager mService;
 
     private final Object mLock = new Object(); // lock access to the following vars
 
@@ -242,14 +248,14 @@
     private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>();
 
     /** @hide */
-    public WifiNanManager(Context context, IWifiNanManager service) {
+    public WifiAwareManager(Context context, IWifiAwareManager service) {
         mContext = context;
         mService = service;
     }
 
     /**
-     * Enable the usage of the NAN API. Doesn't actually turn on NAN cluster formation - that
-     * only happens when an attach is attempted. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast
+     * Enable the usage of the Aware API. Doesn't actually turn on Aware cluster formation - that
+     * only happens when an attach is attempted. {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast
      * will be triggered.
      *
      * @hide
@@ -263,9 +269,9 @@
     }
 
     /**
-     * Disable the usage of the NAN API. All attempts to attach() will be rejected. All open
-     * connections and sessions will be terminated. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast
-     * will be triggered.
+     * Disable the usage of the Aware API. All attempts to attach() will be rejected. All open
+     * connections and sessions will be terminated. {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
+     * broadcast will be triggered.
      *
      * @hide
      */
@@ -278,10 +284,11 @@
     }
 
     /**
-     * Returns the current status of NAN API: whether or not NAN is available. To track changes
-     * in the state of NAN API register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
+     * Returns the current status of Aware API: whether or not Aware is available. To track
+     * changes in the state of Aware API register for the
+     * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast.
      *
-     * @return A boolean indicating whether the app can use the NAN API at this time (true) or
+     * @return A boolean indicating whether the app can use the Aware API at this time (true) or
      * not (false).
      */
     public boolean isAvailable() {
@@ -293,12 +300,13 @@
     }
 
     /**
-     * Returns the characteristics of the Wi-Fi NAN interface: a set of parameters which specify
+     * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
      * limitations on configurations, e.g. the maximum service name length.
      *
-     * @return An object specifying configuration limitations of NAN.
+     * @return An object specifying configuration limitations of Aware.
+     * @hide PROPOSED_AWARE_API
      */
-    public WifiNanCharacteristics getCharacteristics() {
+    public WifiAwareCharacteristics getCharacteristics() {
         try {
             return mService.getCharacteristics();
         } catch (RemoteException e) {
@@ -307,14 +315,14 @@
     }
 
     /**
-     * Attach to the Wi-Fi NAN service - enabling the application to create discovery sessions or
+     * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
      * create connections to peers. The device will attach to an existing cluster if it can find
-     * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+     * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
      * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
-     * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the
-     * Wi-Fi NAN object.
+     * An application <b>must</b> call {@link WifiAwareSession#destroy()} when done with the
+     * Wi-Fi Aware object.
      * <p>
-     * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster
+     * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
      * then this function will simply indicate success immediately using the same {@code
      * attachCallback}.
      *
@@ -322,29 +330,29 @@
      * attachCallback} object. If a null is provided then the application's main thread will be
      *                used.
      * @param attachCallback A callback for attach events, extended from
-     * {@link WifiNanAttachCallback}.
+     * {@link WifiAwareAttachCallback}.
      */
-    public void attach(@Nullable Handler handler, @NonNull WifiNanAttachCallback attachCallback) {
+    public void attach(@Nullable Handler handler, @NonNull WifiAwareAttachCallback attachCallback) {
         attach(handler, null, attachCallback, null);
     }
 
     /**
-     * Attach to the Wi-Fi NAN service - enabling the application to create discovery sessions or
+     * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
      * create connections to peers. The device will attach to an existing cluster if it can find
-     * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+     * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
      * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
-     * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the
-     * Wi-Fi NAN object.
+     * An application <b>must</b> call {@link WifiAwareSession#destroy()} when done with the
+     * Wi-Fi Aware object.
      * <p>
-     * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster
+     * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
      * then this function will simply indicate success immediately using the same {@code
      * attachCallback}.
      * <p>
-     * This version of the API attaches a listener to receive the MAC address of the NAN interface
+     * This version of the API attaches a listener to receive the MAC address of the Aware interface
      * on startup and whenever it is updated (it is randomized at regular intervals for privacy).
      * The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
      * permission to execute this attach request. Otherwise, use the
-     * {@link #attach(Handler, WifiNanAttachCallback)} version. Note that aside from permission
+     * {@link #attach(Handler, WifiAwareAttachCallback)} version. Note that aside from permission
      * requirements this listener will wake up the host at regular intervals causing higher power
      * consumption, do not use it unless the information is necessary (e.g. for OOB discovery).
      *
@@ -352,19 +360,19 @@
      * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the
      *                application's main thread will be used.
      * @param attachCallback A callback for attach events, extended from
-     * {@link WifiNanAttachCallback}.
+     * {@link WifiAwareAttachCallback}.
      * @param identityChangedListener A listener for changed identity, extended from
-     * {@link WifiNanIdentityChangedListener}.
+     * {@link WifiAwareIdentityChangedListener}.
      */
-    public void attach(@Nullable Handler handler, @NonNull WifiNanAttachCallback attachCallback,
-            @NonNull WifiNanIdentityChangedListener identityChangedListener) {
+    public void attach(@Nullable Handler handler, @NonNull WifiAwareAttachCallback attachCallback,
+            @NonNull WifiAwareIdentityChangedListener identityChangedListener) {
         attach(handler, null, attachCallback, identityChangedListener);
     }
 
     /** @hide */
     public void attach(Handler handler, ConfigRequest configRequest,
-            WifiNanAttachCallback attachCallback,
-            WifiNanIdentityChangedListener identityChangedListener) {
+            WifiAwareAttachCallback attachCallback,
+            WifiAwareIdentityChangedListener identityChangedListener) {
         if (VDBG) {
             Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback
                     + ", configRequest=" + configRequest + ", identityChangedListener="
@@ -377,7 +385,7 @@
             try {
                 Binder binder = new Binder();
                 mService.connect(binder, mContext.getOpPackageName(),
-                        new WifiNanEventCallbackProxy(this, looper, binder, attachCallback,
+                        new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback,
                                 identityChangedListener), configRequest,
                         identityChangedListener != null);
             } catch (RemoteException e) {
@@ -399,12 +407,12 @@
 
     /** @hide */
     public void publish(int clientId, Looper looper, PublishConfig publishConfig,
-            WifiNanDiscoverySessionCallback callback) {
+            WifiAwareDiscoverySessionCallback callback) {
         if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig);
 
         try {
             mService.publish(clientId, publishConfig,
-                    new WifiNanDiscoverySessionCallbackProxy(this, looper, true, callback,
+                    new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
                             clientId));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -427,7 +435,7 @@
 
     /** @hide */
     public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig,
-            WifiNanDiscoverySessionCallback callback) {
+            WifiAwareDiscoverySessionCallback callback) {
         if (VDBG) {
             if (VDBG) {
                 Log.v(TAG,
@@ -437,7 +445,7 @@
 
         try {
             mService.subscribe(clientId, subscribeConfig,
-                    new WifiNanDiscoverySessionCallbackProxy(this, looper, false, callback,
+                    new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
                             clientId));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -535,13 +543,13 @@
             type = NETWORK_SPECIFIER_TYPE_1D;
         }
 
-        if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
-                && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+        if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
             throw new IllegalArgumentException(
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
             if (token == null) {
                 throw new IllegalArgumentException(
                         "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
@@ -592,13 +600,13 @@
             type = NETWORK_SPECIFIER_TYPE_2D;
         }
 
-        if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
-                && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+        if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
             throw new IllegalArgumentException(
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
             if (peer == null || peer.length != 6) {
                 throw new IllegalArgumentException(
                         "createNetworkSpecifier: Invalid peer MAC address");
@@ -634,7 +642,7 @@
         return json.toString();
     }
 
-    private static class WifiNanEventCallbackProxy extends IWifiNanEventCallback.Stub {
+    private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
         private static final int CALLBACK_CONNECT_SUCCESS = 0;
         private static final int CALLBACK_CONNECT_FAIL = 1;
         private static final int CALLBACK_IDENTITY_CHANGED = 2;
@@ -643,12 +651,12 @@
         private static final int CALLBACK_RANGING_ABORTED = 5;
 
         private final Handler mHandler;
-        private final WeakReference<WifiNanManager> mNanManager;
+        private final WeakReference<WifiAwareManager> mAwareManager;
         private final Binder mBinder;
         private final Looper mLooper;
 
         RttManager.RttListener getAndRemoveRangingListener(int rangingId) {
-            WifiNanManager mgr = mNanManager.get();
+            WifiAwareManager mgr = mAwareManager.get();
             if (mgr == null) {
                 Log.w(TAG, "getAndRemoveRangingListener: called post GC");
                 return null;
@@ -662,39 +670,40 @@
         }
 
         /**
-         * Constructs a {@link WifiNanAttachCallback} using the specified looper.
+         * Constructs a {@link WifiAwareAttachCallback} using the specified looper.
          * All callbacks will delivered on the thread of the specified looper.
          *
          * @param looper The looper on which to execute the callbacks.
          */
-        WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper, Binder binder,
-                final WifiNanAttachCallback attachCallback,
-                final WifiNanIdentityChangedListener identityChangedListener) {
-            mNanManager = new WeakReference<>(mgr);
+        WifiAwareEventCallbackProxy(WifiAwareManager mgr, Looper looper, Binder binder,
+                final WifiAwareAttachCallback attachCallback,
+                final WifiAwareIdentityChangedListener identityChangedListener) {
+            mAwareManager = new WeakReference<>(mgr);
             mLooper = looper;
             mBinder = binder;
 
-            if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper);
+            if (VDBG) Log.v(TAG, "WifiAwareEventCallbackProxy ctor: looper=" + looper);
             mHandler = new Handler(looper) {
                 @Override
                 public void handleMessage(Message msg) {
                     if (DBG) {
-                        Log.d(TAG, "WifiNanEventCallbackProxy: What=" + msg.what + ", msg=" + msg);
+                        Log.d(TAG, "WifiAwareEventCallbackProxy: What=" + msg.what + ", msg="
+                                + msg);
                     }
 
-                    WifiNanManager mgr = mNanManager.get();
+                    WifiAwareManager mgr = mAwareManager.get();
                     if (mgr == null) {
-                        Log.w(TAG, "WifiNanEventCallbackProxy: handleMessage post GC");
+                        Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
                         return;
                     }
 
                     switch (msg.what) {
                         case CALLBACK_CONNECT_SUCCESS:
                             attachCallback.onAttached(
-                                    new WifiNanSession(mgr, mBinder, msg.arg1));
+                                    new WifiAwareSession(mgr, mBinder, msg.arg1));
                             break;
                         case CALLBACK_CONNECT_FAIL:
-                            mNanManager.clear();
+                            mAwareManager.clear();
                             attachCallback.onAttachFailed();
                             break;
                         case CALLBACK_IDENTITY_CHANGED:
@@ -801,8 +810,8 @@
         }
     }
 
-    private static class WifiNanDiscoverySessionCallbackProxy extends
-            IWifiNanDiscoverySessionCallback.Stub {
+    private static class WifiAwareDiscoverySessionCallbackProxy extends
+            IWifiAwareDiscoverySessionCallback.Stub {
         private static final int CALLBACK_SESSION_STARTED = 0;
         private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1;
         private static final int CALLBACK_SESSION_CONFIG_FAIL = 2;
@@ -815,23 +824,24 @@
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
 
-        private final WeakReference<WifiNanManager> mNanManager;
+        private final WeakReference<WifiAwareManager> mAwareManager;
         private final boolean mIsPublish;
-        private final WifiNanDiscoverySessionCallback mOriginalCallback;
+        private final WifiAwareDiscoverySessionCallback mOriginalCallback;
         private final int mClientId;
 
         private final Handler mHandler;
-        private WifiNanDiscoveryBaseSession mSession;
+        private WifiAwareDiscoveryBaseSession mSession;
 
-        WifiNanDiscoverySessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish,
-                WifiNanDiscoverySessionCallback originalCallback, int clientId) {
-            mNanManager = new WeakReference<>(mgr);
+        WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper,
+                boolean isPublish, WifiAwareDiscoverySessionCallback originalCallback,
+                int clientId) {
+            mAwareManager = new WeakReference<>(mgr);
             mIsPublish = isPublish;
             mOriginalCallback = originalCallback;
             mClientId = clientId;
 
             if (VDBG) {
-                Log.v(TAG, "WifiNanDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish);
+                Log.v(TAG, "WifiAwareDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish);
             }
 
             mHandler = new Handler(looper) {
@@ -839,8 +849,8 @@
                 public void handleMessage(Message msg) {
                     if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
 
-                    if (mNanManager.get() == null) {
-                        Log.w(TAG, "WifiNanDiscoverySessionCallbackProxy: handleMessage post GC");
+                    if (mAwareManager.get() == null) {
+                        Log.w(TAG, "WifiAwareDiscoverySessionCallbackProxy: handleMessage post GC");
                         return;
                     }
 
@@ -858,7 +868,7 @@
                                  * creation failed (as opposed to update
                                  * failing)
                                  */
-                                mNanManager.clear();
+                                mAwareManager.clear();
                             }
                             break;
                         case CALLBACK_SESSION_TERMINATED:
@@ -977,20 +987,20 @@
                         "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
             }
 
-            WifiNanManager mgr = mNanManager.get();
+            WifiAwareManager mgr = mAwareManager.get();
             if (mgr == null) {
                 Log.w(TAG, "onProxySessionStarted: mgr GC'd");
                 return;
             }
 
             if (mIsPublish) {
-                WifiNanPublishDiscoverySession session = new WifiNanPublishDiscoverySession(mgr,
+                WifiAwarePublishDiscoverySession session = new WifiAwarePublishDiscoverySession(mgr,
                         mClientId, sessionId);
                 mSession = session;
                 mOriginalCallback.onPublishStarted(session);
             } else {
-                WifiNanSubscribeDiscoverySession
-                        session = new WifiNanSubscribeDiscoverySession(mgr, mClientId, sessionId);
+                WifiAwareSubscribeDiscoverySession
+                        session = new WifiAwareSubscribeDiscoverySession(mgr, mClientId, sessionId);
                 mSession = session;
                 mOriginalCallback.onSubscribeStarted(session);
             }
@@ -1004,7 +1014,7 @@
             } else {
                 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
             }
-            mNanManager.clear();
+            mAwareManager.clear();
             mOriginalCallback.onSessionTerminated(reason);
         }
     }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
similarity index 65%
rename from wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
index 75c6cb7..610a92c 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
@@ -14,39 +14,40 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.NonNull;
 import android.util.Log;
 
 /**
- * A class representing a NAN publish session. Created when
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
+ * A class representing a Aware publish session. Created when
+ * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig,
+ * WifiAwareDiscoverySessionCallback)}
  * is called and a discovery session is created and returned in
- * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}. See
- * baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. This
+ * {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}. See
+ * baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}. This
  * object allows updating an existing/running publish discovery session using
  * {@link #updatePublish(PublishConfig)}.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanPublishDiscoverySession extends WifiNanDiscoveryBaseSession {
-    private static final String TAG = "WifiNanPublishDiscSsn";
+public class WifiAwarePublishDiscoverySession extends WifiAwareDiscoveryBaseSession {
+    private static final String TAG = "WifiAwarePublishDiscSsn";
 
     /** @hide */
-    public WifiNanPublishDiscoverySession(WifiNanManager manager, int clientId, int sessionId) {
+    public WifiAwarePublishDiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
         super(manager, clientId, sessionId);
     }
 
     /**
      * Re-configure the currently active publish session. The
-     * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used
+     * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
      * at creation is still used. The results of the configuration are returned using
-     * {@link WifiNanDiscoverySessionCallback}:
+     * {@link WifiAwareDiscoverySessionCallback}:
      * <ul>
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
      *     update succeeded.
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
      *     update failed. The publish discovery session is still running using its previous
      *     configuration (i.e. update failure does not terminate the session).
      * </ul>
@@ -58,9 +59,9 @@
             Log.w(TAG, "updatePublish: called on terminated session");
             return;
         } else {
-            WifiNanManager mgr = mMgr.get();
+            WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "updatePublish: called post GC on WifiNanManager");
+                Log.w(TAG, "updatePublish: called post GC on WifiAwareManager");
                 return;
             }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
similarity index 66%
rename from wifi/java/android/net/wifi/nan/WifiNanSession.java
rename to wifi/java/android/net/wifi/aware/WifiAwareSession.java
index df5e3c1..357bd43 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.NetworkRequest;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -29,17 +28,17 @@
 import java.lang.ref.WeakReference;
 
 /**
- * This class represents a Wi-Fi NAN session - an attachment to the Wi-Fi NAN service through
+ * This class represents a Wi-Fi Aware session - an attachment to the Wi-Fi Aware service through
  * which the app can execute discovery operations.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanSession {
-    private static final String TAG = "WifiNanSession";
+public class WifiAwareSession {
+    private static final String TAG = "WifiAwareSession";
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private final WeakReference<WifiNanManager> mMgr;
+    private final WeakReference<WifiAwareManager> mMgr;
     private final Binder mBinder;
     private final int mClientId;
 
@@ -47,7 +46,7 @@
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     /** @hide */
-    public WifiNanSession(WifiNanManager manager, Binder binder, int clientId) {
+    public WifiAwareSession(WifiAwareManager manager, Binder binder, int clientId) {
         if (VDBG) Log.v(TAG, "New session created: manager=" + manager + ", clientId=" + clientId);
 
         mMgr = new WeakReference<>(manager);
@@ -59,19 +58,19 @@
     }
 
     /**
-     * Destroy the Wi-Fi NAN service session and, if no other applications are attached to NAN,
-     * also disable NAN. This method destroys all outstanding operations - i.e. all publish and
+     * Destroy the Wi-Fi Aware service session and, if no other applications are attached to Aware,
+     * also disable Aware. This method destroys all outstanding operations - i.e. all publish and
      * subscribes are terminated, and any outstanding data-links are shut-down. However, it is
      * good practice to destroy these discovery sessions and connections explicitly before a
      * session-wide destroy.
      * <p>
      * An application may re-attach after a destroy using
-     * {@link WifiNanManager#attach(Handler, WifiNanAttachCallback)} .
+     * {@link WifiAwareManager#attach(Handler, WifiAwareAttachCallback)} .
      */
     public void destroy() {
-        WifiNanManager mgr = mMgr.get();
+        WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
-            Log.w(TAG, "destroy: called post GC on WifiNanManager");
+            Log.w(TAG, "destroy: called post GC on WifiAwareManager");
             return;
         }
         mgr.disconnect(mClientId, mBinder);
@@ -94,23 +93,24 @@
     }
 
     /**
-     * Issue a request to the NAN service to create a new NAN publish discovery session, using
+     * Issue a request to the Aware service to create a new Aware publish discovery session, using
      * the specified {@code publishConfig} configuration. The results of the publish operation
-     * are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}:
+     * are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
      * <ul>
      *     <li>
-     *     {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}
+     *     {@link WifiAwareDiscoverySessionCallback#onPublishStarted(
+     *     WifiAwarePublishDiscoverySession)}
      *     is called when the publish session is created and provides a handle to the session.
      *     Further operations on the publish session can be executed on that object.
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
      *     publish operation failed.
      * </ul>
      * <p>
      * Other results of the publish session operations will also be routed to callbacks
      * on the {@code callback} object. The resulting publish session can be modified using
-     * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}.
+     * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
      * <p>
-     *      An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
+     *      An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
      *      terminate the publish discovery session once it isn't needed. This will free
      *      resources as well terminate any on-air transmissions.
      * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
@@ -120,14 +120,14 @@
      * callback} object. If a null is provided then the application's main thread will be used.
      * @param publishConfig The {@link PublishConfig} specifying the
      *            configuration of the requested publish session.
-     * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for
+     * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
      *                 session event callbacks.
      */
     public void publish(@Nullable Handler handler, @NonNull PublishConfig publishConfig,
-            @NonNull WifiNanDiscoverySessionCallback callback) {
-        WifiNanManager mgr = mMgr.get();
+            @NonNull WifiAwareDiscoverySessionCallback callback) {
+        WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
-            Log.e(TAG, "publish: called post GC on WifiNanManager");
+            Log.e(TAG, "publish: called post GC on WifiAwareManager");
             return;
         }
         if (mTerminated) {
@@ -139,23 +139,24 @@
     }
 
     /**
-     * Issue a request to the NAN service to create a new NAN subscribe discovery session, using
+     * Issue a request to the Aware service to create a new Aware subscribe discovery session, using
      * the specified {@code subscribeConfig} configuration. The results of the subscribe
-     * operation are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}:
+     * operation are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
      * <ul>
      *     <li>
-     *  {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}
+     *  {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
+     *  WifiAwareSubscribeDiscoverySession)}
      *     is called when the subscribe session is created and provides a handle to the session.
      *     Further operations on the subscribe session can be executed on that object.
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
      *     subscribe operation failed.
      * </ul>
      * <p>
      * Other results of the subscribe session operations will also be routed to callbacks
      * on the {@code callback} object. The resulting subscribe session can be modified using
-     * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+     * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
      * <p>
-     *      An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
+     *      An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
      *      terminate the subscribe discovery session once it isn't needed. This will free
      *      resources as well terminate any on-air transmissions.
      * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
@@ -165,14 +166,14 @@
      * callback} object. If a null is provided then the application's main thread will be used.
      * @param subscribeConfig The {@link SubscribeConfig} specifying the
      *            configuration of the requested subscribe session.
-     * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for
+     * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
      *                 session event callbacks.
      */
     public void subscribe(@Nullable Handler handler, @NonNull SubscribeConfig subscribeConfig,
-            @NonNull WifiNanDiscoverySessionCallback callback) {
-        WifiNanManager mgr = mMgr.get();
+            @NonNull WifiAwareDiscoverySessionCallback callback) {
+        WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
-            Log.e(TAG, "publish: called post GC on WifiNanManager");
+            Log.e(TAG, "publish: called post GC on WifiAwareManager");
             return;
         }
         if (mTerminated) {
@@ -184,20 +185,20 @@
     }
 
     /**
-     * Create a {@link NetworkRequest.Builder#setNetworkSpecifier(String)} for a
-     * WiFi NAN connection to the specified peer. The
-     * {@link NetworkRequest.Builder#addTransportType(int)} should be set to
-     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
+     * WiFi Aware connection to the specified peer. The
+     * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
-     *     (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer -
-     *     when using NAN discovery use the alternative network specifier method -
-     *     {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
+     *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+     *     when using Aware discovery use the alternative network specifier method -
+     *     {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
      *
      * @param role  The role of this device:
-     *              {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
-     *              {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
-     * @param peer  The MAC address of the peer's NAN discovery interface. On a RESPONDER this
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+     * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
      *              peer. A RESPONDER may specified a null - indicating that it will accept
      *              connection requests from any device.
@@ -209,14 +210,15 @@
      *
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
-     * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest, android.net.ConnectivityManager.NetworkCallback)}
+     * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, @Nullable byte[] peer,
-            @Nullable byte[] token) {
-        WifiNanManager mgr = mMgr.get();
+    public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
+            @Nullable byte[] peer, @Nullable byte[] token) {
+        WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
-            Log.e(TAG, "createNetworkSpecifier: called post GC on WifiNanManager");
+            Log.e(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
             return "";
         }
         if (mTerminated) {
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
similarity index 65%
rename from wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
index f5b4c0c..7c48f54 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
@@ -14,41 +14,43 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 import android.annotation.NonNull;
 import android.util.Log;
 
 /**
- * A class representing a NAN subscribe session. Created when
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
+ * A class representing a Aware subscribe session. Created when
+ * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig,
+ * WifiAwareDiscoverySessionCallback)}
  * is called and a discovery session is created and returned in
- * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}.
- * See baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}.
+ * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(WifiAwareSubscribeDiscoverySession)}.
+ * See baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}.
  * This object allows updating an existing/running subscribe discovery session using
  * {@link #updateSubscribe(SubscribeConfig)}.
  *
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
  */
-public class WifiNanSubscribeDiscoverySession extends WifiNanDiscoveryBaseSession {
-    private static final String TAG = "WifiNanSubscribeDiscSsn";
+public class WifiAwareSubscribeDiscoverySession extends WifiAwareDiscoveryBaseSession {
+    private static final String TAG = "WifiAwareSubsDiscSsn";
 
     /**
      * {@hide}
      */
-    public WifiNanSubscribeDiscoverySession(WifiNanManager manager, int clientId, int sessionId) {
+    public WifiAwareSubscribeDiscoverySession(WifiAwareManager manager, int clientId,
+            int sessionId) {
         super(manager, clientId, sessionId);
     }
 
     /**
      * Re-configure the currently active subscribe session. The
-     * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used
+     * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
      * at creation is still used. The results of the configuration are returned using
-     * {@link WifiNanDiscoverySessionCallback}:
+     * {@link WifiAwareDiscoverySessionCallback}:
      * <ul>
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
      *     update succeeded.
-     *     <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+     *     <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
      *     update failed. The subscribe discovery session is still running using its previous
      *     configuration (i.e. update failure does not terminate the session).
      * </ul>
@@ -61,9 +63,9 @@
             Log.w(TAG, "updateSubscribe: called on terminated session");
             return;
         } else {
-            WifiNanManager mgr = mMgr.get();
+            WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "updateSubscribe: called post GC on WifiNanManager");
+                Log.w(TAG, "updateSubscribe: called post GC on WifiAwareManager");
                 return;
             }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanUtils.java b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
similarity index 93%
rename from wifi/java/android/net/wifi/nan/WifiNanUtils.java
rename to wifi/java/android/net/wifi/aware/WifiAwareUtils.java
index c0f36b4..4083388 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanUtils.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.aware;
 
 /**
- * Provides utilities for the Wifi NAN manager/service.
+ * Provides utilities for the Wifi Aware manager/service.
  *
  * @hide
  */
-public class WifiNanUtils {
+public class WifiAwareUtils {
     /**
      * Per spec: The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. The
      * only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric values (A-Z,
diff --git a/wifi/java/android/net/wifi/aware/package.html b/wifi/java/android/net/wifi/aware/package.html
new file mode 100644
index 0000000..1a990d8
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/package.html
@@ -0,0 +1,43 @@
+<HTML>
+<BODY>
+<p>Provides classes which allow applications to use Wi-Fi Aware to discover peers and create
+    connections to them.</p>
+<p>Using the Wi-Fi Aware APIs, applications can advertise services, discover peers which are
+    advertising services, and connect to them.
+    Wi-Fi Aware is independent of Wi-Fi infrastructure (i.e. a device may or may
+    not be associated with an AP concurrent to using Wi-Fi Aware). </p>
+<p>The primary entry point to Wi-Fi Aware capabilities is the
+    {@link android.net.wifi.aware.WifiAwareManager} class, which is acquired by calling
+    {@link android.content.Context#getSystemService(String)
+    Context.getSystemService(Context.WIFI_AWARE_SERVICE)}</p>
+
+<p>Some APIs may require the following user permissions:</p>
+<ul>
+    <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
+    <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
+    <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi Aware
+    functionality.
+    If your application only works with Wi-Fi Aware (i.e. it should only be installed on devices which
+    support Wi-Fi Aware), declare so with a <a
+            href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+        {@code &lt;uses-feature&gt;}</a>
+    element in the manifest file:</p>
+<pre>
+&lt;manifest ...>
+    &lt;uses-feature android:name="android.hardware.wifi.aware" />
+    ...
+&lt;/manifest>
+</pre>
+<p>Alternatively, if you application does not require Wi-Fi Aware but can take advantage of it if
+    available, you can perform
+    the check at run-time in your code using {@link
+    android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
+    android.content.pm.PackageManager#FEATURE_WIFI_AWARE}:</p>
+<pre>
+    getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
+</pre>
+</BODY>
+</HTML>
diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java
new file mode 100644
index 0000000..96db5d0
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java
@@ -0,0 +1,473 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.omadm.PPSMOParser;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class for building PasspointConfiguration from an installation file.
+ *
+ * @hide
+ */
+public final class ConfigBuilder {
+    private static final String TAG = "ConfigBuilder";
+
+    // Header names.
+    private static final String CONTENT_TYPE = "Content-Type";
+    private static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
+
+    // MIME types.
+    private static final String TYPE_MULTIPART_MIXED = "multipart/mixed";
+    private static final String TYPE_WIFI_CONFIG = "application/x-wifi-config";
+    private static final String TYPE_PASSPOINT_PROFILE = "application/x-passpoint-profile";
+    private static final String TYPE_CA_CERT = "application/x-x509-ca-cert";
+    private static final String TYPE_PKCS12 = "application/x-pkcs12";
+
+    private static final String ENCODING_BASE64 = "base64";
+    private static final String BOUNDARY = "boundary=";
+
+    /**
+     * Class represent a MIME (Multipurpose Internet Mail Extension) part.
+     */
+    private static class MimePart {
+        /**
+         * Content type of the part.
+         */
+        public String type = null;
+
+        /**
+         * Decoded data.
+         */
+        public byte[] data = null;
+
+        /**
+         * Flag indicating if this is the last part (ending with --{boundary}--).
+         */
+        public boolean isLast = false;
+    }
+
+    /**
+     * Class represent the MIME (Multipurpose Internet Mail Extension) header.
+     */
+    private static class MimeHeader {
+        /**
+         * Content type.
+         */
+        public String contentType = null;
+
+        /**
+         * Boundary string (optional), only applies for the outter MIME header.
+         */
+        public String boundary = null;
+
+        /**
+         * Encoding type.
+         */
+        public String encodingType = null;
+    }
+
+
+    /**
+     * Parse the Hotspot 2.0 Release 1 configuration data into a {@link PasspointConfiguration}
+     * object.  The configuration data is a base64 encoded MIME multipart data.  Below is
+     * the format of the decoded message:
+     *
+     * Content-Type: multipart/mixed; boundary={boundary}
+     * Content-Transfer-Encoding: base64
+     *
+     * --{boundary}
+     * Content-Type: application/x-passpoint-profile
+     * Content-Transfer-Encoding: base64
+     *
+     * [base64 encoded Passpoint profile data]
+     * --{boundary}
+     * Content-Type: application/x-x509-ca-cert
+     * Content-Transfer-Encoding: base64
+     *
+     * [base64 encoded X509 CA certificate data]
+     * --{boundary}
+     * Content-Type: application/x-pkcs12
+     * Content-Transfer-Encoding: base64
+     *
+     * [base64 encoded PKCS#12 ASN.1 structure containing client certificate chain]
+     * --{boundary}
+     *
+     * @param mimeType MIME type of the encoded data.
+     * @param data A base64 encoded MIME multipart message containing the Passpoint profile
+     *             (required), CA (Certificate Authority) certificate (optional), and client
+     *             certificate chain (optional).
+     * @return {@link PasspointConfiguration}
+     */
+    public static PasspointConfiguration buildPasspointConfig(String mimeType, byte[] data) {
+        // Verify MIME type.
+        if (!TextUtils.equals(mimeType, TYPE_WIFI_CONFIG)) {
+            Log.e(TAG, "Unexpected MIME type: " + mimeType);
+            return null;
+        }
+
+        try {
+            // Decode the data.
+            byte[] decodedData = Base64.decode(new String(data, StandardCharsets.ISO_8859_1),
+                    Base64.DEFAULT);
+            Map<String, byte[]> mimeParts = parseMimeMultipartMessage(new LineNumberReader(
+                    new InputStreamReader(new ByteArrayInputStream(decodedData),
+                            StandardCharsets.ISO_8859_1)));
+            return createPasspointConfig(mimeParts);
+        } catch (IOException | IllegalArgumentException e) {
+            Log.e(TAG, "Failed to parse installation file: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Create a {@link PasspointConfiguration} object from list of MIME (Multipurpose Internet
+     * Mail Extension) parts.
+     *
+     * @param mimeParts Map of content type and content data.
+     * @return {@link PasspointConfiguration}
+     * @throws IOException
+     */
+    private static PasspointConfiguration createPasspointConfig(Map<String, byte[]> mimeParts)
+            throws IOException {
+        byte[] profileData = mimeParts.get(TYPE_PASSPOINT_PROFILE);
+        if (profileData == null) {
+            throw new IOException("Missing Passpoint Profile");
+        }
+
+        PasspointConfiguration config = PPSMOParser.parseMOText(new String(profileData));
+        if (config == null) {
+            throw new IOException("Failed to parse Passpoint profile");
+        }
+
+        // Credential is needed for storing the certificates and private client key.
+        if (config.credential == null) {
+            throw new IOException("Passpoint profile missing credential");
+        }
+
+        // Parse CA (Certificate Authority) certificate.
+        byte[] caCertData = mimeParts.get(TYPE_CA_CERT);
+        if (caCertData != null) {
+            try {
+                config.credential.caCertificate = parseCACert(caCertData);
+            } catch (CertificateException e) {
+                throw new IOException("Failed to parse CA Certificate");
+            }
+        }
+
+        // Parse PKCS12 data for client private key and certificate chain.
+        byte[] pkcs12Data = mimeParts.get(TYPE_PKCS12);
+        if (pkcs12Data != null) {
+            try {
+                Pair<PrivateKey, List<X509Certificate>> clientKey = parsePkcs12(pkcs12Data);
+                config.credential.clientPrivateKey = clientKey.first;
+                config.credential.clientCertificateChain =
+                        clientKey.second.toArray(new X509Certificate[clientKey.second.size()]);
+            } catch(GeneralSecurityException | IOException e) {
+                throw new IOException("Failed to parse PCKS12 string");
+            }
+        }
+        return config;
+    }
+
+    /**
+     * Parse a MIME (Multipurpose Internet Mail Extension) multipart message from the given
+     * input stream.
+     *
+     * @param in The input stream for reading the message data
+     * @return A map of a content type and content data pair
+     * @throws IOException
+     */
+    private static Map<String, byte[]> parseMimeMultipartMessage(LineNumberReader in)
+            throws IOException {
+        // Parse the outer MIME header.
+        MimeHeader header = parseHeaders(in);
+        if (!TextUtils.equals(header.contentType, TYPE_MULTIPART_MIXED)) {
+            throw new IOException("Invalid content type: " + header.contentType);
+        }
+        if (TextUtils.isEmpty(header.boundary)) {
+            throw new IOException("Missing boundary string");
+        }
+        if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) {
+            throw new IOException("Unexpected encoding: " + header.encodingType);
+        }
+
+        // Read pass the first boundary string.
+        for (;;) {
+            String line = in.readLine();
+            if (line == null) {
+                throw new IOException("Unexpected EOF before first boundary @ " +
+                        in.getLineNumber());
+            }
+            if (line.equals("--" + header.boundary)) {
+                break;
+            }
+        }
+
+        // Parse each MIME part.
+        Map<String, byte[]> mimeParts = new HashMap<>();
+        boolean isLast = false;
+        do {
+            MimePart mimePart = parseMimePart(in, header.boundary);
+            mimeParts.put(mimePart.type, mimePart.data);
+            isLast = mimePart.isLast;
+        } while(!isLast);
+        return mimeParts;
+    }
+
+    /**
+     * Parse a MIME (Multipurpose Internet Mail Extension) part.  We expect the data to
+     * be encoded in base64.
+     *
+     * @param in Input stream to read the data from
+     * @param boundary Boundary string indicate the end of the part
+     * @return {@link MimePart}
+     * @throws IOException
+     */
+    private static MimePart parseMimePart(LineNumberReader in, String boundary)
+            throws IOException {
+        MimeHeader header = parseHeaders(in);
+        // Expect encoding type to be base64.
+        if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) {
+            throw new IOException("Unexpected encoding type: " + header.encodingType);
+        }
+
+        // Check for a valid content type.
+        if (!TextUtils.equals(header.contentType, TYPE_PASSPOINT_PROFILE) &&
+                !TextUtils.equals(header.contentType, TYPE_CA_CERT) &&
+                !TextUtils.equals(header.contentType, TYPE_PKCS12)) {
+            throw new IOException("Unexpected content type: " + header.contentType);
+        }
+
+        StringBuilder text = new StringBuilder();
+        boolean isLast = false;
+        String partBoundary = "--" + boundary;
+        String endBoundary = partBoundary + "--";
+        for (;;) {
+            String line = in.readLine();
+            if (line == null) {
+                throw new IOException("Unexpected EOF file in body @ " + in.getLineNumber());
+            }
+            // Check for boundary line.
+            if (line.startsWith(partBoundary)) {
+                if (line.equals(endBoundary)) {
+                    isLast = true;
+                }
+                break;
+            }
+            text.append(line);
+        }
+
+        MimePart part = new MimePart();
+        part.type = header.contentType;
+        part.data = Base64.decode(text.toString(), Base64.DEFAULT);
+        part.isLast = isLast;
+        return part;
+    }
+
+    /**
+     * Parse a MIME (Multipurpose Internet Mail Extension) header from the input stream.
+     * @param in Input stream to read from.
+     * @return {@link MimeHeader}
+     * @throws IOException
+     */
+    private static MimeHeader parseHeaders(LineNumberReader in)
+            throws IOException {
+        MimeHeader header = new MimeHeader();
+
+        // Read the header from the input stream.
+        Map<String, String> headers = readHeaders(in);
+
+        // Parse each header.
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            switch (entry.getKey()) {
+                case CONTENT_TYPE:
+                    Pair<String, String> value = parseContentType(entry.getValue());
+                    header.contentType = value.first;
+                    header.boundary = value.second;
+                    break;
+                case CONTENT_TRANSFER_ENCODING:
+                    header.encodingType = entry.getValue();
+                    break;
+                default:
+                    throw new IOException("Unexpected header: " + entry.getKey());
+            }
+        }
+        return header;
+    }
+
+    /**
+     * Parse the Content-Type header value.  The value will contain the content type string and
+     * an optional boundary string separated by a ";".  Below are examples of valid Content-Type
+     * header value:
+     *   multipart/mixed; boundary={boundary}
+     *   application/x-passpoint-profile
+     *
+     * @param contentType The Content-Type value string
+     * @return A pair of content type and boundary string
+     * @throws IOException
+     */
+    private static Pair<String, String> parseContentType(String contentType) throws IOException {
+        String[] attributes = contentType.toString().split(";");
+        String type = null;
+        String boundary = null;
+
+        if (attributes.length < 1 || attributes.length > 2) {
+            throw new IOException("Invalid Content-Type: " + contentType);
+        }
+
+        type = attributes[0].trim();
+        if (attributes.length == 2) {
+            boundary = attributes[1].trim();
+            if (!boundary.startsWith(BOUNDARY)) {
+                throw new IOException("Invalid Content-Type: " + contentType);
+            }
+            boundary = boundary.substring(BOUNDARY.length());
+            // Remove the leading and trailing quote if present.
+            if (boundary.length() > 1 && boundary.startsWith("\"") && boundary.endsWith("\"")) {
+                boundary = boundary.substring(1, boundary.length()-1);
+            }
+        }
+
+        return new Pair<String, String>(type, boundary);
+    }
+
+    /**
+     * Read the headers from the given input stream.  The header section is terminated by
+     * an empty line.
+     *
+     * @param in The input stream to read from
+     * @return Map of key-value pairs.
+     * @throws IOException
+     */
+    private static Map<String, String> readHeaders(LineNumberReader in)
+            throws IOException {
+        Map<String, String> headers = new HashMap<>();
+        String line;
+        String name = null;
+        StringBuilder value = null;
+        for (;;) {
+            line = in.readLine();
+            if (line == null) {
+                throw new IOException("Missing line @ " + in.getLineNumber());
+            }
+
+            // End of headers section.
+            if (line.length() == 0 || line.trim().length() == 0) {
+                // Save the previous header line.
+                if (name != null) {
+                    headers.put(name, value.toString());
+                }
+                break;
+            }
+
+            int nameEnd = line.indexOf(':');
+            if (nameEnd < 0) {
+                if (value != null) {
+                    // Continuation line for the header value.
+                    value.append(' ').append(line.trim());
+                } else {
+                    throw new IOException("Bad header line: '" + line + "' @ " +
+                            in.getLineNumber());
+                }
+            } else {
+                // New header line detected, make sure it doesn't start with a whitespace.
+                if (Character.isWhitespace(line.charAt(0))) {
+                    throw new IOException("Illegal blank prefix in header line '" + line +
+                            "' @ " + in.getLineNumber());
+                }
+
+                if (name != null) {
+                    // Save the previous header line.
+                    headers.put(name, value.toString());
+                }
+
+                // Setup the current header line.
+                name = line.substring(0, nameEnd).trim();
+                value = new StringBuilder();
+                value.append(line.substring(nameEnd+1).trim());
+            }
+        }
+        return headers;
+    }
+
+    /**
+     * Parse a CA (Certificate Authority) certificate data and convert it to a
+     * X509Certificate object.
+     *
+     * @param octets Certificate data
+     * @return X509Certificate
+     * @throws CertificateException
+     */
+    private static X509Certificate parseCACert(byte[] octets) throws CertificateException {
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(octets));
+    }
+
+    private static Pair<PrivateKey, List<X509Certificate>> parsePkcs12(byte[] octets)
+            throws GeneralSecurityException, IOException {
+        KeyStore ks = KeyStore.getInstance("PKCS12");
+        ByteArrayInputStream in = new ByteArrayInputStream(octets);
+        ks.load(in, new char[0]);
+        in.close();
+
+        // Only expects one set of key and certificate chain.
+        if (ks.size() != 1) {
+            throw new IOException("Unexpected key size: " + ks.size());
+        }
+
+        String alias = ks.aliases().nextElement();
+        if (alias == null) {
+            throw new IOException("No alias found");
+        }
+
+        PrivateKey clientKey = (PrivateKey) ks.getKey(alias, null);
+        List<X509Certificate> clientCertificateChain = null;
+        Certificate[] chain = ks.getCertificateChain(alias);
+        if (chain != null) {
+            clientCertificateChain = new ArrayList<>();
+            for (Certificate certificate : chain) {
+                if (!(certificate instanceof X509Certificate)) {
+                    throw new IOException("Unexpceted certificate type: " +
+                            certificate.getClass());
+                }
+                clientCertificateChain.add((X509Certificate) certificate);
+            }
+        }
+        return new Pair<PrivateKey, List<X509Certificate>>(clientKey, clientCertificateChain);
+    }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
similarity index 73%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
index 5f66d16..6b1cea8 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
+/**
+ * Copyright (c) 2016, 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
+ *     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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.hotspot2;
 
-parcelable PublishConfig;
+parcelable PasspointConfiguration;
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
new file mode 100644
index 0000000..a62a0fb
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Class representing Passpoint configuration.  This contains configurations specified in
+ * PerProviderSubscription (PPS) Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently, only HomeSP and Credential subtrees are supported.
+ *
+ * @hide
+ */
+public final class PasspointConfiguration implements Parcelable {
+    public HomeSP homeSp = null;
+    public Credential credential = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(homeSp, flags);
+        dest.writeParcelable(credential, flags);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof PasspointConfiguration)) {
+            return false;
+        }
+        PasspointConfiguration that = (PasspointConfiguration) thatObject;
+        return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) &&
+                (credential == null ? that.credential == null :
+                    credential.equals(that.credential));
+    }
+
+    /**
+     * Validate the configuration data.
+     *
+     * @return true on success or false on failure
+     */
+    public boolean validate() {
+        if (homeSp == null || !homeSp.validate()) {
+            return false;
+        }
+        if (credential == null || !credential.validate()) {
+            return false;
+        }
+        return true;
+    }
+
+    public static final Creator<PasspointConfiguration> CREATOR =
+        new Creator<PasspointConfiguration>() {
+            @Override
+            public PasspointConfiguration createFromParcel(Parcel in) {
+                PasspointConfiguration config = new PasspointConfiguration();
+                config.homeSp = in.readParcelable(null);
+                config.credential = in.readParcelable(null);
+                return config;
+            }
+            @Override
+            public PasspointConfiguration[] newArray(int size) {
+                return new PasspointConfiguration[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
new file mode 100644
index 0000000..65a49ea
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
@@ -0,0 +1,786 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2.omadm;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management)
+ * PPS-MO (PerProviderSubscription Management Object) XML tree to a
+ * {@link PasspointConfiguration} object.
+ *
+ * Currently this only supports PerProviderSubscription/HomeSP and
+ * PerProviderSubscription/Credential subtree for Hotspot 2.0 Release 1 support.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Below is a sample XML string for a Release 1 PPS MO tree:
+ *
+ * <MgmtTree xmlns="syncml:dmddf1.2">
+ *   <VerDTD>1.2</VerDTD>
+ *   <Node>
+ *     <NodeName>PerProviderSubscription</NodeName>
+ *     <RTProperties>
+ *       <Type>
+ *         <DDFName>urn:wfa:mo:hotspot2dot0­perprovidersubscription:1.0</DDFName>
+ *       </Type>
+ *     </RTProperties>
+ *     <Node>
+ *       <NodeName>i001</NodeName>
+ *       <Node>
+ *         <NodeName>HomeSP</NodeName>
+ *         <Node>
+ *           <NodeName>FriendlyName</NodeName>
+ *           <Value>Century House</Value>
+ *         </Node>
+ *         <Node>
+ *           <NodeName>FQDN</NodeName>
+ *           <Value>mi6.co.uk</Value>
+ *         </Node>
+ *         <Node>
+ *           <NodeName>RoamingConsortiumOI</NodeName>
+ *           <Value>112233,445566</Value>
+ *         </Node>
+ *       </Node>
+ *       <Node>
+ *         <NodeName>Credential</NodeName>
+ *         <Node>
+ *           <NodeName>Realm</NodeName>
+ *           <Value>shaken.stirred.com</Value>
+ *         </Node>
+ *         <Node>
+ *           <NodeName>UsernamePassword</NodeName>
+ *           <Node>
+ *             <NodeName>Username</NodeName>
+ *             <Value>james</Value>
+ *           </Node>
+ *           <Node>
+ *             <NodeName>Password</NodeName>
+ *             <Value>Ym9uZDAwNw==</Value>
+ *           </Node>
+ *           <Node>
+ *             <NodeName>EAPMethod</NodeName>
+ *             <Node>
+ *               <NodeName>EAPType</NodeName>
+ *               <Value>21</Value>
+ *             </Node>
+ *             <Node>
+ *               <NodeName>InnerMethod</NodeName>
+ *               <Value>MS-CHAP-V2</Value>
+ *             </Node>
+ *           </Node>
+ *         </Node>
+ *       </Node>
+ *     </Node>
+ *   </Node>
+ * </MgmtTree>
+ *
+ * @hide
+ */
+public final class PPSMOParser {
+    private static final String TAG = "PPSMOParser";
+
+    /**
+     * XML tags expected in the PPS MO (PerProviderSubscription Management Object) XML tree.
+     */
+    private static final String TAG_MANAGEMENT_TREE = "MgmtTree";
+    private static final String TAG_VER_DTD = "VerDTD";
+    private static final String TAG_NODE = "Node";
+    private static final String TAG_NODE_NAME = "NodeName";
+    private static final String TAG_RT_PROPERTIES = "RTProperties";
+    private static final String TAG_TYPE = "Type";
+    private static final String TAG_DDF_NAME = "DDFName";
+    private static final String TAG_VALUE = "Value";
+
+    /**
+     * Name for PerProviderSubscription node.
+     */
+    private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription";
+
+    /**
+     * Fields under HomeSP subtree.
+     */
+    private static final String NODE_HOMESP = "HomeSP";
+    private static final String NODE_FQDN = "FQDN";
+    private static final String NODE_FRIENDLY_NAME = "FriendlyName";
+    private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI";
+
+    /**
+     * Fields under Credential subtree.
+     */
+    private static final String NODE_CREDENTIAL = "Credential";
+    private static final String NODE_USERNAME_PASSWORD = "UsernamePassword";
+    private static final String NODE_USERNAME = "Username";
+    private static final String NODE_PASSWORD = "Password";
+    private static final String NODE_EAP_METHOD = "EAPMethod";
+    private static final String NODE_EAP_TYPE = "EAPType";
+    private static final String NODE_INNER_METHOD = "InnerMethod";
+    private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate";
+    private static final String NODE_CERTIFICATE_TYPE = "CertificateType";
+    private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint";
+    private static final String NODE_REALM = "Realm";
+    private static final String NODE_SIM = "SIM";
+    private static final String NODE_SIM_IMSI = "IMSI";
+
+    /**
+     * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree.
+     */
+    private static final String PPS_MO_URN =
+            "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0";
+
+    /**
+     * Exception for generic parsing errors.
+     */
+    private static class ParsingException extends Exception {
+        public ParsingException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Class representing a node within the PerProviderSubscription tree.
+     * This is used to flatten out and eliminate the extra layering in the XMLNode tree,
+     * to make the data parsing easier and cleaner.
+     *
+     * A PPSNode can be an internal or a leaf node, but not both.
+     *
+     */
+    private static abstract class PPSNode {
+        private final String mName;
+        public PPSNode(String name) {
+            mName = name;
+        }
+
+        /**
+         * @return the name of the node
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Applies for internal node only.
+         *
+         * @return the list of children nodes.
+         */
+        public abstract List<PPSNode> getChildren();
+
+        /**
+         * Applies for leaf node only.
+         *
+         * @return the string value of the node
+         */
+        public abstract String getValue();
+
+        /**
+         * @return a flag indicating if this is a leaf or an internal node
+         */
+        public abstract boolean isLeaf();
+    }
+
+    /**
+     * Class representing a leaf node in a PPS (PerProviderSubscription) tree.
+     */
+    private static class LeafNode extends PPSNode {
+        private final String mValue;
+        public LeafNode(String nodeName, String value) {
+            super(nodeName);
+            mValue = value;
+        }
+
+        @Override
+        public String getValue() {
+            return mValue;
+        }
+
+        @Override
+        public List<PPSNode> getChildren() {
+            return null;
+        }
+
+        @Override
+        public boolean isLeaf() {
+            return true;
+        }
+    }
+
+    /**
+     * Class representing an internal node in a PPS (PerProviderSubscription) tree.
+     */
+    private static class InternalNode extends PPSNode {
+        private final List<PPSNode> mChildren;
+        public InternalNode(String nodeName, List<PPSNode> children) {
+            super(nodeName);
+            mChildren = children;
+        }
+
+        @Override
+        public String getValue() {
+            return null;
+        }
+
+        @Override
+        public List<PPSNode> getChildren() {
+            return mChildren;
+        }
+
+        @Override
+        public boolean isLeaf() {
+            return false;
+        }
+    }
+
+    /**
+     * Convert a XML string representation of a PPS MO (PerProviderSubscription
+     * Management Object) tree to a {@link PasspointConfiguration} object.
+     *
+     * @param xmlString XML string representation of a PPS MO tree
+     * @return {@link PasspointConfiguration} or null
+     */
+    public static PasspointConfiguration parseMOText(String xmlString) {
+        // Convert the XML string to a XML tree.
+        XMLParser xmlParser = new XMLParser();
+        XMLNode root = null;
+        try {
+            root = xmlParser.parse(xmlString);
+        } catch(IOException | SAXException e) {
+            return null;
+        }
+        if (root == null) {
+            return null;
+        }
+
+        // Verify root node is a "MgmtTree" node.
+        if (root.getTag() != TAG_MANAGEMENT_TREE) {
+            Log.e(TAG, "Root is not a MgmtTree");
+            return null;
+        }
+
+        String verDtd = null;    // Used for detecting duplicate VerDTD element.
+        PasspointConfiguration config = null;
+        for (XMLNode child : root.getChildren()) {
+            switch(child.getTag()) {
+                case TAG_VER_DTD:
+                    if (verDtd != null) {
+                        Log.e(TAG, "Duplicate VerDTD element");
+                        return null;
+                    }
+                    verDtd = child.getText();
+                    break;
+                case TAG_NODE:
+                    if (config != null) {
+                        Log.e(TAG, "Unexpected multiple Node element under MgmtTree");
+                        return null;
+                    }
+                    try {
+                        config = parsePpsNode(child);
+                    } catch (ParsingException e) {
+                        Log.e(TAG, e.getMessage());
+                        return null;
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unknown node: " + child.getTag());
+                    return null;
+            }
+        }
+        return config;
+    }
+
+    /**
+     * Parse a PerProviderSubscription node. Below is the format of the XML tree (with
+     * each XML element represent a node in the tree):
+     *
+     * <Node>
+     *   <NodeName>PerProviderSubscription</NodeName>
+     *   <RTProperties>
+     *     ...
+     *   </RTPProperties>
+     *   <Node>
+     *     ...
+     *   </Node>
+     * </Node>
+     *
+     * @param node XMLNode that contains PerProviderSubscription node.
+     * @return PasspointConfiguration or null
+     * @throws ParsingException
+     */
+    private static PasspointConfiguration parsePpsNode(XMLNode node)
+            throws ParsingException {
+        PasspointConfiguration config = null;
+        String nodeName = null;
+        for (XMLNode child : node.getChildren()) {
+            switch (child.getTag()) {
+                case TAG_NODE_NAME:
+                    if (nodeName != null) {
+                        throw new ParsingException("Duplicant NodeName: " + child.getText());
+                    }
+                    nodeName = child.getText();
+                    if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) {
+                        throw new ParsingException("Unexpected NodeName: " + nodeName);
+                    }
+                    break;
+                case TAG_NODE:
+                    // Only one PerProviderSubscription instance is expected and allowed.
+                    if (config != null) {
+                        throw new ParsingException("Multiple PPS instance");
+                    }
+                    // Convert the XML tree to a PPS tree.
+                    PPSNode ppsInstanceRoot = buildPpsNode(child);
+                    config = parsePpsInstance(ppsInstanceRoot);
+                    break;
+                case TAG_RT_PROPERTIES:
+                    // Parse and verify URN stored in the RT (Run Time) Properties.
+                    String urn = parseUrn(child);
+                    if (!TextUtils.equals(urn, PPS_MO_URN)) {
+                        throw new ParsingException("Unknown URN: " + urn);
+                    }
+                    break;
+                default:
+                    throw new ParsingException("Unknown tag under PPS node: " + child.getTag());
+            }
+        }
+        return config;
+    }
+
+    /**
+     * Parse the URN stored in the RTProperties. Below is the format of the RTPProperties node:
+     *
+     * <RTProperties>
+     *   <Type>
+     *     <DDFName>urn:...</DDFName>
+     *   </Type>
+     * </RTProperties>
+     *
+     * @param node XMLNode that contains RTProperties node.
+     * @return URN String of URN.
+     * @throws ParsingException
+     */
+    private static String parseUrn(XMLNode node) throws ParsingException {
+        if (node.getChildren().size() != 1)
+            throw new ParsingException("Expect RTPProperties node to only have one child");
+
+        XMLNode typeNode = node.getChildren().get(0);
+        if (typeNode.getChildren().size() != 1) {
+            throw new ParsingException("Expect Type node to only have one child");
+        }
+        if (!TextUtils.equals(typeNode.getTag(), TAG_TYPE)) {
+            throw new ParsingException("Unexpected tag for Type: " + typeNode.getTag());
+        }
+
+        XMLNode ddfNameNode = typeNode.getChildren().get(0);
+        if (!ddfNameNode.getChildren().isEmpty()) {
+            throw new ParsingException("Expect DDFName node to have no child");
+        }
+        if (!TextUtils.equals(ddfNameNode.getTag(), TAG_DDF_NAME)) {
+            throw new ParsingException("Unexpected tag for DDFName: " + ddfNameNode.getTag());
+        }
+
+        return ddfNameNode.getText();
+    }
+
+    /**
+     * Convert a XML tree represented by XMLNode to a PPS (PerProviderSubscription) instance tree
+     * represented by PPSNode.  This flattens out the XML tree to allow easier and cleaner parsing
+     * of the PPS configuration data.  Only three types of XML tag are expected: "NodeName",
+     * "Node", and "Value".
+     *
+     * The original XML tree (each XML element represent a node):
+     *
+     * <Node>
+     *   <NodeName>root</NodeName>
+     *   <Node>
+     *     <NodeName>child1</NodeName>
+     *     <Value>value1</Value>
+     *   </Node>
+     *   <Node>
+     *     <NodeName>child2</NodeName>
+     *     <Node>
+     *       <NodeName>grandchild1</NodeName>
+     *       ...
+     *     </Node>
+     *   </Node>
+     *   ...
+     * </Node>
+     *
+     * The converted PPS tree:
+     *
+     * [root] --- [child1, value1]
+     *   |
+     *   ---------[child2] --------[grandchild1] --- ...
+     *
+     * @param node XMLNode pointed to the root of a XML tree
+     * @return PPSNode pointing to the root of a PPS tree
+     * @throws ParsingException
+     */
+    private static PPSNode buildPpsNode(XMLNode node) throws ParsingException {
+        String nodeName = null;
+        String nodeValue = null;
+        List<PPSNode> childNodes = new ArrayList<PPSNode>();
+        // Names of parsed child nodes, use for detecting multiple child nodes with the same name.
+        Set<String> parsedNodes = new HashSet<String>();
+
+        for (XMLNode child : node.getChildren()) {
+            String tag = child.getTag();
+            if (TextUtils.equals(tag, TAG_NODE_NAME)) {
+                if (nodeName != null) {
+                    throw new ParsingException("Duplicate NodeName node");
+                }
+                nodeName = child.getText();
+            } else if (TextUtils.equals(tag, TAG_NODE)) {
+                PPSNode ppsNode = buildPpsNode(child);
+                if (parsedNodes.contains(ppsNode.getName())) {
+                    throw new ParsingException("Duplicate node: " + ppsNode.getName());
+                }
+                parsedNodes.add(ppsNode.getName());
+                childNodes.add(ppsNode);
+            } else if (TextUtils.equals(tag, TAG_VALUE)) {
+               if (nodeValue != null) {
+                   throw new ParsingException("Duplicate Value node");
+               }
+               nodeValue = child.getText();
+            } else {
+                throw new ParsingException("Unknown tag: " + tag);
+            }
+        }
+
+        if (nodeName == null) {
+            throw new ParsingException("Invalid node: missing NodeName");
+        }
+        if (nodeValue == null && childNodes.size() == 0) {
+            throw new ParsingException("Invalid node: " + nodeName +
+                    " missing both value and children");
+        }
+        if (nodeValue != null && childNodes.size() > 0) {
+            throw new ParsingException("Invalid node: " + nodeName +
+                    " contained both value and children");
+        }
+
+        if (nodeValue != null) {
+            return new LeafNode(nodeName, nodeValue);
+        }
+        return new InternalNode(nodeName, childNodes);
+    }
+
+    /**
+     * Return the value of a PPSNode.  An exception will be thrown if the given node
+     * is not a leaf node.
+     *
+     * @param node PPSNode to retrieve the value from
+     * @return String representing the value of the node
+     * @throws ParsingException
+     */
+    private static String getPpsNodeValue(PPSNode node) throws ParsingException {
+        if (!node.isLeaf()) {
+            throw new ParsingException("Cannot get value from a non-leaf node: " + node.getName());
+        }
+        return node.getValue();
+    }
+
+    /**
+     * Parse a PPS (PerProviderSubscription) configurations from a PPS tree.
+     *
+     * @param root PPSNode representing the root of the PPS tree
+     * @return PasspointConfiguration
+     * @throws ParsingException
+     */
+    private static PasspointConfiguration parsePpsInstance(PPSNode root)
+            throws ParsingException {
+        if (root.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for PPS instance");
+        }
+
+        PasspointConfiguration config = new PasspointConfiguration();
+        for (PPSNode child : root.getChildren()) {
+            switch(child.getName()) {
+                case NODE_HOMESP:
+                    config.homeSp = parseHomeSP(child);
+                    break;
+                case NODE_CREDENTIAL:
+                    config.credential = parseCredential(child);
+                    break;
+                default:
+                    throw new ParsingException("Unknown node: " + child.getName());
+            }
+        }
+        return config;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP subtree.
+     *
+     * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP subtree
+     * @return HomeSP
+     * @throws ParsingException
+     */
+    private static HomeSP parseHomeSP(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for HomeSP");
+        }
+
+        HomeSP homeSp = new HomeSP();
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_FQDN:
+                    homeSp.fqdn = getPpsNodeValue(child);
+                    break;
+                case NODE_FRIENDLY_NAME:
+                    homeSp.friendlyName = getPpsNodeValue(child);
+                    break;
+                case NODE_ROAMING_CONSORTIUM_OI:
+                    homeSp.roamingConsortiumOIs =
+                            parseRoamingConsortiumOI(getPpsNodeValue(child));
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under HomeSP: " + child.getName());
+            }
+        }
+        return homeSp;
+    }
+
+    /**
+     * Parse the roaming consortium OI string, which contains a list of OIs separated by ",".
+     *
+     * @param oiStr string containing list of OIs (Organization Identifiers) separated by ","
+     * @return long[]
+     * @throws ParsingException
+     */
+    private static long[] parseRoamingConsortiumOI(String oiStr)
+            throws ParsingException {
+        String[] oiStrArray = oiStr.split(",");
+        long[] oiArray = new long[oiStrArray.length];
+        for (int i = 0; i < oiStrArray.length; i++) {
+            try {
+                oiArray[i] = Long.parseLong(oiStrArray[i], 16);
+            } catch (NumberFormatException e) {
+                throw new ParsingException("Invalid OI: " + oiStrArray[i]);
+            }
+        }
+        return oiArray;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Credential subtree.
+     *
+     * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree
+     * @return Credential
+     * @throws ParsingException
+     */
+    private static Credential parseCredential(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for HomeSP");
+        }
+
+        Credential credential = new Credential();
+        for (PPSNode child: node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_USERNAME_PASSWORD:
+                    credential.userCredential = parseUserCredential(child);
+                    break;
+                case NODE_DIGITAL_CERTIFICATE:
+                    credential.certCredential = parseCertificateCredential(child);
+                    break;
+                case NODE_REALM:
+                    credential.realm = getPpsNodeValue(child);
+                    break;
+                case NODE_SIM:
+                    credential.simCredential = parseSimCredential(child);
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under Credential: " +
+                            child.getName());
+            }
+        }
+        return credential;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Credential/UsernamePassword subtree.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/Credential/UsernamePassword subtree
+     * @return Credential.UserCredential
+     * @throws ParsingException
+     */
+    private static Credential.UserCredential parseUserCredential(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for UsernamePassword");
+        }
+
+        Credential.UserCredential userCred = new Credential.UserCredential();
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_USERNAME:
+                    userCred.username = getPpsNodeValue(child);
+                    break;
+                case NODE_PASSWORD:
+                    userCred.password = getPpsNodeValue(child);
+                    break;
+                case NODE_EAP_METHOD:
+                    parseEAPMethod(child, userCred);
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under UsernamPassword: " +
+                            child.getName());
+            }
+        }
+        return userCred;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Credential/UsernamePassword/EAPMethod
+     * subtree.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/Credential/UsernamePassword/EAPMethod subtree
+     * @param userCred UserCredential to be updated with EAP method values.
+     * @throws ParsingException
+     */
+    private static void parseEAPMethod(PPSNode node, Credential.UserCredential userCred)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for EAPMethod");
+        }
+
+        for (PPSNode child : node.getChildren()) {
+            switch(child.getName()) {
+                case NODE_EAP_TYPE:
+                    userCred.eapType = parseInteger(getPpsNodeValue(child));
+                    break;
+                case NODE_INNER_METHOD:
+                    userCred.nonEapInnerMethod = getPpsNodeValue(child);
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under EAPMethod: " + child.getName());
+            }
+        }
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Credential/DigitalCertificate subtree.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/Credential/DigitalCertificate subtree
+     * @return Credential.CertificateCredential
+     * @throws ParsingException
+     */
+    private static Credential.CertificateCredential parseCertificateCredential(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for DigitalCertificate");
+        }
+
+        Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_CERTIFICATE_TYPE:
+                    certCred.certType = getPpsNodeValue(child);
+                    break;
+                case NODE_CERT_SHA256_FINGERPRINT:
+                    certCred.certSha256FingerPrint = parseHexString(getPpsNodeValue(child));
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under DigitalCertificate: " +
+                            child.getName());
+            }
+        }
+        return certCred;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/Credential/SIM subtree.
+     *
+     * @param node PPSNode representing the root of the PerProviderSubscription/Credential/SIM
+     *             subtree
+     * @return Credential.SimCredential
+     * @throws ParsingException
+     */
+    private static Credential.SimCredential parseSimCredential(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for SIM");
+        }
+
+        Credential.SimCredential simCred = new Credential.SimCredential();
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_SIM_IMSI:
+                    simCred.imsi = getPpsNodeValue(child);
+                    break;
+                case NODE_EAP_TYPE:
+                    simCred.eapType = parseInteger(getPpsNodeValue(child));
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under SIM: " + child.getName());
+            }
+        }
+        return simCred;
+    }
+
+    /**
+     * Convert a hex string to a byte array.
+     *
+     * @param str String containing hex values
+     * @return byte[]
+     * @throws ParsingException
+     */
+    private static byte[] parseHexString(String str) throws ParsingException {
+        if ((str.length() & 1) == 1) {
+            throw new ParsingException("Odd length hex string: " + str.length());
+        }
+
+        byte[] result = new byte[str.length() / 2];
+        for (int i = 0; i < result.length; i++) {
+          int index = i * 2;
+          try {
+              result[i] = (byte) Integer.parseInt(str.substring(index, index + 2), 16);
+          } catch (NumberFormatException e) {
+              throw new ParsingException("Invalid hex string: " + str);
+          }
+        }
+        return result;
+    }
+
+    /**
+     * Parse an integer string.
+     *
+     * @param value String of integer value
+     * @return int
+     * @throws ParsingException
+     */
+    private static int parseInteger(String value) throws ParsingException {
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            throw new ParsingException("Invalid integer value: " + value);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
new file mode 100644
index 0000000..e87698c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2.omadm;
+
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class represent a node in an XML tree. Each node is an XML element.
+ * Used by {@link XMLParser} for parsing/converting each XML element to XMLNode.
+ *
+ * @hide
+ */
+public class XMLNode {
+    private final String mTag;
+    private final List<XMLNode> mChildren;
+    private final XMLNode mParent;
+    private StringBuilder mTextBuilder;
+    private String mText;
+
+    public XMLNode(XMLNode parent, String tag) {
+        mTag = tag;
+        mParent = parent;
+        mChildren = new ArrayList<>();
+        mTextBuilder = new StringBuilder();
+        mText = null;
+    }
+
+    /**
+     * Adding a text to this node. Invoked by {@link XMLParser#characters}.
+     *
+     * @param text String to be added
+     */
+    public void addText(String text) {
+        mTextBuilder.append(text);
+    }
+
+    /**
+     * Adding a child node to this node. Invoked by {@link XMLParser#startElement}.
+     *
+     * @param child XMLNode to be added
+     */
+    public void addChild(XMLNode child) {
+        mChildren.add(child);
+    }
+
+    /**
+     * Invoked when the end of the XML element is detected. Used for further processing
+     * of the text enclosed within this XML element. Invoked by {@link XMLParser#endElement}.
+     */
+    public void close() {
+        // Remove the leading and the trailing whitespaces.
+        mText = mTextBuilder.toString().trim();
+        mTextBuilder = null;
+    }
+
+    public String getTag() {
+        return mTag;
+    }
+
+    public XMLNode getParent() {
+        return mParent;
+    }
+
+    public String getText() {
+        return mText;
+    }
+
+    public List<XMLNode> getChildren() {
+        return mChildren;
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof XMLNode)) {
+            return false;
+        }
+        XMLNode that = (XMLNode) thatObject;
+
+        return TextUtils.equals(mTag, that.mTag) &&
+                TextUtils.equals(mText, that.mText) &&
+                mChildren.equals(that.mChildren);
+    }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
new file mode 100644
index 0000000..948052c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.text.TextUtils;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Class for parsing an XML string to an XML tree represented by {@link XMLNode}.
+ *
+ * The original XML string:
+ * <root>
+ *   <tag1>text1</tag1>
+ *   <tag2>
+ *     <tag3>text3</tag3>
+ *   </tag2>
+ * </root>
+ *
+ * The XML tree representation:
+ *                  [root]
+ *                     |
+ *                     |
+ *   [tag1, text1]-----|-----[tag2]
+ *                             |
+ *                             |
+ *                       [tag3, text3]
+ *
+ * @hide
+ */
+public class XMLParser extends DefaultHandler {
+    private XMLNode mRoot = null;
+    private XMLNode mCurrent = null;
+
+    public XMLNode parse(String text) throws IOException, SAXException {
+        if (TextUtils.isEmpty(text)) {
+            throw new IOException("XML string not provided");
+        }
+
+        // Reset pointers.
+        mRoot = null;
+        mCurrent = null;
+
+        try {
+            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+            parser.parse(new InputSource(new StringReader(text)), this);
+            return mRoot;
+        } catch (ParserConfigurationException pce) {
+            throw new SAXException(pce);
+        }
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes)
+            throws SAXException {
+        XMLNode parent = mCurrent;
+
+        mCurrent = new XMLNode(parent, qName);
+
+        if (mRoot == null) {
+            mRoot = mCurrent;
+        } else if (parent == null) {
+            throw new SAXException("More than one root nodes");
+        } else {
+            parent.addChild(mCurrent);
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (!qName.equals(mCurrent.getTag())) {
+            throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
+                    mCurrent);
+        }
+
+        mCurrent.close();
+        mCurrent = mCurrent.getParent();
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        mCurrent.addText(new String(ch, start, length));
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
index 5f66d16..3d8e833 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
+/**
+ * Copyright (c) 2016, 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
+ *     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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.hotspot2.pps;
 
-parcelable PublishConfig;
+parcelable Credential;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
new file mode 100644
index 0000000..57e65eb
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -0,0 +1,688 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2.pps;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.ParcelUtil;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class representing Credential subtree in the PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * In addition to the fields in the Credential subtree, this will also maintain necessary
+ * information for the private key and certificates associated with this credential.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+    private static final String TAG = "Credential";
+
+    /**
+     * Max string length for realm.  Refer to Credential/Realm node in Hotspot 2.0 Release 2
+     * Technical Specification Section 9.1 for more info.
+     */
+    private static final int MAX_REALM_LENGTH = 253;
+
+    /**
+     * The realm associated with this credential.  It will be used to determine
+     * if this credential can be used to authenticate with a given hotspot by
+     * comparing the realm specified in that hotspot's ANQP element.
+     */
+    public String realm = null;
+
+    /**
+     * Username-password based credential.
+     * Contains the fields under PerProviderSubscription/Credential/UsernamePassword subtree.
+     */
+    public static final class UserCredential implements Parcelable {
+        /**
+         * Maximum string length for username.  Refer to Credential/UsernamePassword/Username
+         * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
+         */
+        private static final int MAX_USERNAME_LENGTH = 63;
+
+        /**
+         * Maximum string length for password.  Refer to Credential/UsernamePassword/Password
+         * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
+         */
+        private static final int MAX_PASSWORD_LENGTH = 255;
+
+        /**
+         * Supported Non-EAP inner methods.  Refer to
+         * Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical
+         * Specification Section 9.1 for more info.
+         */
+        private static final Set<String> SUPPORTED_AUTH =
+                new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2"));
+
+        /**
+         * Username of the credential.
+         */
+        public String username = null;
+
+        /**
+         * Base64-encoded password.
+         */
+        public String password = null;
+
+        /**
+         * EAP (Extensible Authentication Protocol) method type.
+         * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+         * for valid values.
+         * Using Integer.MIN_VALUE to indicate unset value.
+         */
+        public int eapType = Integer.MIN_VALUE;
+
+        /**
+         * Non-EAP inner authentication method.
+         */
+        public String nonEapInnerMethod = null;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(username);
+            dest.writeString(password);
+            dest.writeInt(eapType);
+            dest.writeString(nonEapInnerMethod);
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof UserCredential)) {
+                return false;
+            }
+
+            UserCredential that = (UserCredential) thatObject;
+            return TextUtils.equals(username, that.username) &&
+                    TextUtils.equals(password, that.password) &&
+                    eapType == that.eapType &&
+                    TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
+        }
+
+        /**
+         * Validate the configuration data.
+         *
+         * @return true on success or false on failure
+         */
+        public boolean validate() {
+            if (TextUtils.isEmpty(username)) {
+                Log.d(TAG, "Missing username");
+                return false;
+            }
+            if (username.length() > MAX_USERNAME_LENGTH) {
+                Log.d(TAG, "username exceeding maximum length: " + username.length());
+                return false;
+            }
+
+            if (TextUtils.isEmpty(password)) {
+                Log.d(TAG, "Missing password");
+                return false;
+            }
+            if (password.length() > MAX_PASSWORD_LENGTH) {
+                Log.d(TAG, "password exceeding maximum length: " + password.length());
+                return false;
+            }
+
+            // Only supports EAP-TTLS for user credential.
+            if (eapType != EAPConstants.EAP_TTLS) {
+                Log.d(TAG, "Invalid EAP Type for user credential: " + eapType);
+                return false;
+            }
+
+            // Verify Non-EAP inner method for EAP-TTLS.
+            if (!SUPPORTED_AUTH.contains(nonEapInnerMethod)) {
+                Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + nonEapInnerMethod);
+                return false;
+            }
+            return true;
+        }
+
+        public static final Creator<UserCredential> CREATOR =
+            new Creator<UserCredential>() {
+                @Override
+                public UserCredential createFromParcel(Parcel in) {
+                    UserCredential userCredential = new UserCredential();
+                    userCredential.username = in.readString();
+                    userCredential.password = in.readString();
+                    userCredential.eapType = in.readInt();
+                    userCredential.nonEapInnerMethod = in.readString();
+                    return userCredential;
+                }
+
+                @Override
+                public UserCredential[] newArray(int size) {
+                    return new UserCredential[size];
+                }
+            };
+    }
+    public UserCredential userCredential = null;
+
+    /**
+     * Certificate based credential.  This is used for EAP-TLS.
+     * Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree.
+     */
+    public static final class CertificateCredential implements Parcelable {
+        /**
+         * Supported certificate types.
+         */
+        private static final String CERT_TYPE_X509V3 = "x509v3";
+
+        /**
+         * Certificate SHA-256 fingerprint length.
+         */
+        private static final int CERT_SHA256_FINGER_PRINT_LENGTH = 32;
+
+        /**
+         * Certificate type.
+         */
+        public String certType = null;
+
+        /**
+         * The SHA-256 fingerprint of the certificate.
+         */
+        public byte[] certSha256FingerPrint = null;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(certType);
+            dest.writeByteArray(certSha256FingerPrint);
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof CertificateCredential)) {
+                return false;
+            }
+
+            CertificateCredential that = (CertificateCredential) thatObject;
+            return TextUtils.equals(certType, that.certType) &&
+                    Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
+        }
+
+        /**
+         * Validate the configuration data.
+         *
+         * @return true on success or false on failure
+         */
+        public boolean validate() {
+            if (!TextUtils.equals(CERT_TYPE_X509V3, certType)) {
+                Log.d(TAG, "Unsupported certificate type: " + certType);
+                return false;
+            }
+            if (certSha256FingerPrint == null ||
+                    certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) {
+                Log.d(TAG, "Invalid SHA-256 fingerprint");
+                return false;
+            }
+            return true;
+        }
+
+        public static final Creator<CertificateCredential> CREATOR =
+            new Creator<CertificateCredential>() {
+                @Override
+                public CertificateCredential createFromParcel(Parcel in) {
+                    CertificateCredential certCredential = new CertificateCredential();
+                    certCredential.certType = in.readString();
+                    certCredential.certSha256FingerPrint = in.createByteArray();
+                    return certCredential;
+                }
+
+                @Override
+                public CertificateCredential[] newArray(int size) {
+                    return new CertificateCredential[size];
+                }
+            };
+    }
+    public CertificateCredential certCredential = null;
+
+    /**
+     * SIM (Subscriber Identify Module) based credential.
+     * Contains fields under PerProviderSubscription/Credential/SIM subtree.
+     */
+    public static final class SimCredential implements Parcelable {
+        /**
+         * Maximum string length for IMSI.
+         */
+        public static final int MAX_IMSI_LENGTH = 15;
+
+        /**
+         * International Mobile Subscriber Identity, is used to identify the user
+         * of a cellular network and is a unique identification associated with all
+         * cellular networks
+         */
+        public String imsi = null;
+
+        /**
+         * EAP (Extensible Authentication Protocol) method type for using SIM credential.
+         * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+         * for valid values.
+         * Using Integer.MIN_VALUE to indicate unset value.
+         */
+        public int eapType = Integer.MIN_VALUE;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof SimCredential)) {
+                return false;
+            }
+
+            SimCredential that = (SimCredential) thatObject;
+            return TextUtils.equals(imsi, that.imsi) &&
+                    eapType == that.eapType;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(imsi);
+            dest.writeInt(eapType);
+        }
+
+        /**
+         * Validate the configuration data.
+         *
+         * @return true on success or false on failure
+         */
+        public boolean validate() {
+            // Note: this only validate the format of IMSI string itself.  Additional verification
+            // will be done by WifiService at the time of provisioning to verify against the IMSI
+            // of the SIM card installed in the device.
+            if (!verifyImsi()) {
+                return false;
+            }
+            if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA &&
+                    eapType != EAPConstants.EAP_AKA_PRIME) {
+                Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType);
+                return false;
+            }
+            return true;
+        }
+
+        public static final Creator<SimCredential> CREATOR =
+            new Creator<SimCredential>() {
+                @Override
+                public SimCredential createFromParcel(Parcel in) {
+                    SimCredential simCredential = new SimCredential();
+                    simCredential.imsi = in.readString();
+                    simCredential.eapType = in.readInt();
+                    return simCredential;
+                }
+
+                @Override
+                public SimCredential[] newArray(int size) {
+                    return new SimCredential[size];
+                }
+            };
+
+        /**
+         * Verify the IMSI (International Mobile Subscriber Identity) string.  The string
+         * should contain zero or more numeric digits, and might ends with a "*" for prefix
+         * matching.
+         *
+         * @return true if IMSI is valid, false otherwise.
+         */
+        private boolean verifyImsi() {
+            if (TextUtils.isEmpty(imsi)) {
+                Log.d(TAG, "Missing IMSI");
+                return false;
+            }
+            if (imsi.length() > MAX_IMSI_LENGTH) {
+                Log.d(TAG, "IMSI exceeding maximum length: " + imsi.length());
+                return false;
+            }
+
+            // Locate the first non-digit character.
+            int nonDigit;
+            char stopChar = '\0';
+            for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
+                stopChar = imsi.charAt(nonDigit);
+                if (stopChar < '0' || stopChar > '9') {
+                    break;
+                }
+            }
+
+            if (nonDigit == imsi.length()) {
+                return true;
+            }
+            else if (nonDigit == imsi.length()-1 && stopChar == '*') {
+                // Prefix matching.
+                return true;
+            }
+            return false;
+        }
+    }
+    public SimCredential simCredential = null;
+
+    /**
+     * CA (Certificate Authority) X509 certificate.
+     */
+    public X509Certificate caCertificate = null;
+
+    /**
+     * Client side X509 certificate chain.
+     */
+    public X509Certificate[] clientCertificateChain = null;
+
+    /**
+     * Client side private key.
+     */
+    public PrivateKey clientPrivateKey = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(realm);
+        dest.writeParcelable(userCredential, flags);
+        dest.writeParcelable(certCredential, flags);
+        dest.writeParcelable(simCredential, flags);
+        ParcelUtil.writeCertificate(dest, caCertificate);
+        ParcelUtil.writeCertificates(dest, clientCertificateChain);
+        ParcelUtil.writePrivateKey(dest, clientPrivateKey);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof Credential)) {
+            return false;
+        }
+
+        Credential that = (Credential) thatObject;
+        return TextUtils.equals(realm, that.realm) &&
+                (userCredential == null ? that.userCredential == null :
+                    userCredential.equals(that.userCredential)) &&
+                (certCredential == null ? that.certCredential == null :
+                    certCredential.equals(that.certCredential)) &&
+                (simCredential == null ? that.simCredential == null :
+                    simCredential.equals(that.simCredential)) &&
+                isX509CertificateEquals(caCertificate, that.caCertificate) &&
+                isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain) &&
+                isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
+    }
+
+    /**
+     * Validate the configuration data.
+     *
+     * @return true on success or false on failure
+     */
+    public boolean validate() {
+        if (TextUtils.isEmpty(realm)) {
+            Log.d(TAG, "Missing realm");
+            return false;
+        }
+        if (realm.length() > MAX_REALM_LENGTH) {
+            Log.d(TAG, "realm exceeding maximum length: " + realm.length());
+            return false;
+        }
+
+        // Verify the credential.
+        if (userCredential != null) {
+            if (!verifyUserCredential()) {
+                return false;
+            }
+        } else if (certCredential != null) {
+            if (!verifyCertCredential()) {
+                return false;
+            }
+        } else if (simCredential != null) {
+            if (!verifySimCredential()) {
+                return false;
+            }
+        } else {
+            Log.d(TAG, "Missing required credential");
+            return false;
+        }
+
+        return true;
+    }
+
+    public static final Creator<Credential> CREATOR =
+        new Creator<Credential>() {
+            @Override
+            public Credential createFromParcel(Parcel in) {
+                Credential credential = new Credential();
+                credential.realm = in.readString();
+                credential.userCredential = in.readParcelable(null);
+                credential.certCredential = in.readParcelable(null);
+                credential.simCredential = in.readParcelable(null);
+                credential.caCertificate = ParcelUtil.readCertificate(in);
+                credential.clientCertificateChain = ParcelUtil.readCertificates(in);
+                credential.clientPrivateKey = ParcelUtil.readPrivateKey(in);
+                return credential;
+            }
+
+            @Override
+            public Credential[] newArray(int size) {
+                return new Credential[size];
+            }
+        };
+
+    /**
+     * Verify user credential.
+     *
+     * @return true if user credential is valid, false otherwise.
+     */
+    private boolean verifyUserCredential() {
+        if (userCredential == null) {
+            Log.d(TAG, "Missing user credential");
+            return false;
+        }
+        if (certCredential != null || simCredential != null) {
+            Log.d(TAG, "Contained more than one type of credential");
+            return false;
+        }
+        if (!userCredential.validate()) {
+            return false;
+        }
+        if (caCertificate == null) {
+            Log.d(TAG, "Missing CA Certificate for user credential");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Verify certificate credential, which is used for EAP-TLS.  This will verify
+     * that the necessary client key and certificates are provided.
+     *
+     * @return true if certificate credential is valid, false otherwise.
+     */
+    private boolean verifyCertCredential() {
+        if (certCredential == null) {
+            Log.d(TAG, "Missing certificate credential");
+            return false;
+        }
+        if (userCredential != null || simCredential != null) {
+            Log.d(TAG, "Contained more than one type of credential");
+            return false;
+        }
+
+        if (!certCredential.validate()) {
+            return false;
+        }
+
+        // Verify required key and certificates for certificate credential.
+        if (caCertificate == null) {
+            Log.d(TAG, "Missing CA Certificate for certificate credential");
+            return false;
+        }
+        if (clientPrivateKey == null) {
+            Log.d(TAG, "Missing client private key for certificate credential");
+            return false;
+        }
+        try {
+            // Verify SHA-256 fingerprint for client certificate.
+            if (!verifySha256Fingerprint(clientCertificateChain,
+                    certCredential.certSha256FingerPrint)) {
+                Log.d(TAG, "SHA-256 fingerprint mismatch");
+                return false;
+            }
+        } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+            Log.d(TAG, "Failed to verify SHA-256 fingerprint: " + e.getMessage());
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Verify SIM credential.
+     *
+     * @return true if SIM credential is valid, false otherwise.
+     */
+    private boolean verifySimCredential() {
+        if (simCredential == null) {
+            Log.d(TAG, "Missing SIM credential");
+            return false;
+        }
+        if (userCredential != null || certCredential != null) {
+            Log.d(TAG, "Contained more than one type of credential");
+            return false;
+        }
+        return simCredential.validate();
+    }
+
+    private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) {
+        if (key1 == null && key2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (key1 == null || key2 == null) {
+            return false;
+        }
+
+        return TextUtils.equals(key1.getAlgorithm(), key2.getAlgorithm()) &&
+                Arrays.equals(key1.getEncoded(), key2.getEncoded());
+    }
+
+    private static boolean isX509CertificateEquals(X509Certificate cert1, X509Certificate cert2) {
+        if (cert1 == null && cert2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (cert1 == null || cert2 == null) {
+            return false;
+        }
+
+        boolean result = false;
+        try {
+            result = Arrays.equals(cert1.getEncoded(), cert2.getEncoded());
+        } catch (CertificateEncodingException e) {
+            /* empty, return false. */
+        }
+        return result;
+    }
+
+    private static boolean isX509CertificatesEquals(X509Certificate[] certs1,
+                                                    X509Certificate[] certs2) {
+        if (certs1 == null && certs2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (certs1 == null || certs2 == null) {
+            return false;
+        }
+
+        if (certs1.length != certs2.length) {
+            return false;
+        }
+
+        for (int i = 0; i < certs1.length; i++) {
+            if (!isX509CertificateEquals(certs1[i], certs2[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Verify that the digest for a certificate in the certificate chain matches expected
+     * fingerprint.  The certificate that matches the fingerprint is the client certificate.
+     *
+     * @param certChain Chain of certificates
+     * @param expectedFingerprint The expected SHA-256 digest of the client certificate
+     * @return true if the certificate chain contains a matching certificate, false otherwise
+     * @throws NoSuchAlgorithmException
+     * @throws CertificateEncodingException
+     */
+    private static boolean verifySha256Fingerprint(X509Certificate[] certChain,
+                                                   byte[] expectedFingerprint)
+            throws NoSuchAlgorithmException, CertificateEncodingException {
+        if (certChain == null) {
+            return false;
+        }
+        MessageDigest digester = MessageDigest.getInstance("SHA-256");
+        for (X509Certificate certificate : certChain) {
+            digester.reset();
+            byte[] fingerprint = digester.digest(certificate.getEncoded());
+            if (Arrays.equals(expectedFingerprint, fingerprint)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
similarity index 75%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
index 5f66d16..62d5603 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
+/**
+ * Copyright (c) 2016, 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
+ *     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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi.nan;
+package android.net.wifi.hotspot2.pps;
 
-parcelable PublishConfig;
+parcelable HomeSP;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
new file mode 100644
index 0000000..5837c06
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2016, 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 android.net.wifi.hotspot2.pps;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Class representing HomeSP subtree in PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class HomeSP implements Parcelable {
+    private static final String TAG = "HomeSP";
+
+    /**
+     * FQDN (Fully Qualified Domain Name) of this home service provider.
+     */
+    public String fqdn = null;
+
+    /**
+     * Friendly name of this home service provider.
+     */
+    public String friendlyName = null;
+
+    /**
+     * List of Organization Identifiers (OIs) identifying a roaming consortium of
+     * which this provider is a member.
+     */
+    public long[] roamingConsortiumOIs = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(fqdn);
+        dest.writeString(friendlyName);
+        dest.writeLongArray(roamingConsortiumOIs);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof HomeSP)) {
+            return false;
+        }
+        HomeSP that = (HomeSP) thatObject;
+
+        return TextUtils.equals(fqdn, that.fqdn) &&
+                TextUtils.equals(friendlyName, that.friendlyName) &&
+                Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
+    }
+
+    /**
+     * Validate HomeSP data.
+     *
+     * @return true on success or false on failure
+     */
+    public boolean validate() {
+        if (TextUtils.isEmpty(fqdn)) {
+            Log.d(TAG, "Missing FQDN");
+            return false;
+        }
+        if (TextUtils.isEmpty(friendlyName)) {
+            Log.d(TAG, "Missing friendly name");
+            return false;
+        }
+        return true;
+    }
+
+    public static final Creator<HomeSP> CREATOR =
+        new Creator<HomeSP>() {
+            @Override
+            public HomeSP createFromParcel(Parcel in) {
+                HomeSP homeSp = new HomeSP();
+                homeSp.fqdn = in.readString();
+                homeSp.friendlyName = in.readString();
+                homeSp.roamingConsortiumOIs = in.createLongArray();
+                return homeSp;
+            }
+
+            @Override
+            public HomeSP[] newArray(int size) {
+                return new HomeSP[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java b/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java
deleted file mode 100644
index d8c310b..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.net.wifi.nan;
-
-/**
- * Base class for NAN attach callbacks. Should be extended by applications and set when calling
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}. These are callbacks
- * applying to the NAN connection as a whole - not to specific publish or subscribe sessions -
- * for that see {@link WifiNanDiscoverySessionCallback}.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanAttachCallback {
-    /**
-     * Called when NAN attach operation
-     * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}
-     * is completed and that we can now start discovery sessions or connections.
-     *
-     * @param session The NAN object on which we can execute further NAN operations - e.g.
-     *                discovery, connections.
-     */
-    public void onAttached(WifiNanSession session) {
-        /* empty */
-    }
-
-    /**
-     * Called when NAN attach operation
-     * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)} failed.
-     */
-    public void onAttachFailed() {
-        /* empty */
-    }
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
deleted file mode 100644
index e562a00..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.net.wifi.nan;
-
-parcelable WifiNanCharacteristics;
diff --git a/wifi/java/android/net/wifi/nan/package.html b/wifi/java/android/net/wifi/nan/package.html
deleted file mode 100644
index ae3cf6c..0000000
--- a/wifi/java/android/net/wifi/nan/package.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides classes which allow applications to use Wi-Fi NAN to discover peers and create
-    connections to them.</p>
-<p>Using the Wi-Fi NAN APIs, applications can advertise services, discover peers which are
-    advertising services, and connect to them.
-    Wi-Fi NAN is independent of Wi-Fi infrastructure (i.e. a device may or may
-    not be associated with an AP concurrent to using Wi-Fi NAN). </p>
-<p>The primary entry point to Wi-Fi NAN capabilities is the
-    {@link android.net.wifi.nan.WifiNanManager} class, which is acquired by calling
-    {@link android.content.Context#getSystemService(String)
-    Context.getSystemService(Context.WIFI_NAN_SERVICE)}</p>
-
-<p>Some APIs may require the following user permissions:</p>
-<ul>
-    <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
-    <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
-    <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
-</ul>
-
-<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi NAN
-    functionality.
-    If your application only works with Wi-Fi NAN (i.e. it should only be installed on devices which
-    support Wi-Fi NAN), declare so with a <a
-            href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
-        {@code &lt;uses-feature&gt;}</a>
-    element in the manifest file:</p>
-<pre>
-&lt;manifest ...>
-    &lt;uses-feature android:name="android.hardware.wifi.nan" />
-    ...
-&lt;/manifest>
-</pre>
-<p>Alternatively, if you application does not require Wi-Fi NAN but can take advantage of it if
-    available, you can perform
-    the check at run-time in your code using {@link
-    android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
-    android.content.pm.PackageManager#FEATURE_WIFI_NAN}:</p>
-<pre>
-    getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)
-</pre>
-</BODY>
-</HTML>
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
index ee2e895..8b1cfae 100644
--- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -28,5 +28,6 @@
     Messenger getMessenger();
     Messenger getP2pStateMachineMessenger();
     void setMiracastMode(int mode);
+    void checkConfigureWifiDisplayPermission();
 }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 8d5cf63..c93ac7b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -283,6 +283,13 @@
     public static final String EXTRA_HANDOVER_MESSAGE =
             "android.net.wifi.p2p.EXTRA_HANDOVER_MESSAGE";
 
+    /**
+     * The lookup key for a calling package returned by the WifiP2pService.
+     * @hide
+     */
+    public static final String CALLING_PACKAGE =
+            "android.net.wifi.p2p.CALLING_PACKAGE";
+
     IWifiP2pManager mService;
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
@@ -1271,7 +1278,10 @@
      */
     public void requestPeers(Channel c, PeerListListener listener) {
         checkChannel(c);
-        c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
+        Bundle callingPackage = new Bundle();
+        callingPackage.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
+        c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener),
+                callingPackage);
     }
 
     /**
@@ -1314,6 +1324,11 @@
             Channel c, WifiP2pWfdInfo wfdInfo,
             ActionListener listener) {
         checkChannel(c);
+        try {
+            mService.checkConfigureWifiDisplayPermission();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
         c.mAsyncChannel.sendMessage(SET_WFD_INFO, 0, c.putListener(listener), wfdInfo);
     }
 
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
new file mode 100644
index 0000000..8c1eb08
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
new file mode 100644
index 0000000..6d86dd5
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -0,0 +1,73 @@
+Content-Type: multipart/mixed; boundary={boundary}
+Content-Transfer-Encoding: base64
+
+--{boundary}
+Content-Type: application/x-passpoint-profile
+Content-Transfer-Encoding: base64
+
+PE1nbXRUcmVlIHhtbG5zPSJzeW5jbWw6ZG1kZGYxLjIiPgogIDxWZXJEVEQ+MS4yPC9WZXJEVEQ+
+CiAgPE5vZGU+CiAgICA8Tm9kZU5hbWU+UGVyUHJvdmlkZXJTdWJzY3JpcHRpb248L05vZGVOYW1l
+PgogICAgPFJUUHJvcGVydGllcz4KICAgICAgPFR5cGU+CiAgICAgICAgPERERk5hbWU+dXJuOndm
+YTptbzpob3RzcG90MmRvdDAtcGVycHJvdmlkZXJzdWJzY3JpcHRpb246MS4wPC9EREZOYW1lPgog
+ICAgICA8L1R5cGU+CiAgICA8L1JUUHJvcGVydGllcz4KICAgIDxOb2RlPgogICAgICA8Tm9kZU5h
+bWU+aTAwMTwvTm9kZU5hbWU+CiAgICAgIDxOb2RlPgogICAgICAgIDxOb2RlTmFtZT5Ib21lU1A8
+L05vZGVOYW1lPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZyaWVuZGx5TmFt
+ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+Q2VudHVyeSBIb3VzZTwvVmFsdWU+CiAgICAg
+ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZRRE48L05vZGVO
+YW1lPgogICAgICAgICAgPFZhbHVlPm1pNi5jby51azwvVmFsdWU+CiAgICAgICAgPC9Ob2RlPgog
+ICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25zb3J0aXVtT0k8L05v
+ZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVlPgogICAgICAgIDwv
+Tm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9kZU5hbWU+Q3JlZGVu
+dGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+UmVhbG08
+L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPnNoYWtlbi5zdGlycmVkLmNvbTwvVmFsdWU+CiAg
+ICAgICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlVzZXJuYW1l
+UGFzc3dvcmQ8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt
+ZT5Vc2VybmFtZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5qYW1lczwvVmFsdWU+CiAg
+ICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1lPlBh
+c3N3b3JkPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPlltOXVaREF3Tnc9PTwvVmFsdWU+
+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
+PkVBUE1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
+b2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgICA8VmFsdWU+MjE8L1ZhbHVl
+PgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
+b2RlTmFtZT5Jbm5lck1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPk1TLUNI
+QVAtVjI8L1ZhbHVlPgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8L05vZGU+CiAgICAg
+ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0
+aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
+PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh
+bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
+TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
+MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
+ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg
+IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k
+ZT4KICAgICAgICAgICAgPE5vZGVOYW1lPklNU0k8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFs
+dWU+aW1zaTwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAg
+ICAgICAgPE5vZGVOYW1lPkVBUFR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+MjQ8
+L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgog
+ICAgPC9Ob2RlPgogIDwvTm9kZT4KPC9NZ210VHJlZT4K
+
+--{boundary}
+Content-Type: application/x-x509-ca-cert
+Content-Transfer-Encoding: base64
+
+LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLRENDQWhDZ0F3SUJBZ0lKQUlMbEZkd3pM
+VnVyTUEwR0NTcUdTSWIzRFFFQkN3VUFNQkl4RURBT0JnTlYKQkFNVEIwVkJVQ0JEUVRFd0hoY05N
+VFl3TVRFeU1URTFNREUxV2hjTk1qWXdNVEE1TVRFMU1ERTFXakFTTVJBdwpEZ1lEVlFRREV3ZEZR
+VkFnUTBFeE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCnpuQVBV
+ejI2TXNhZTR3czQzY3pSNDEvSjJRdHJTSVpVS21WVXNWdW1EYllIclBOdlRYS1NNWEFjZXdPUkRR
+WVgKUnF2SHZwbjhDc2NCMStvR1hadkh3eGo0elYwV0tvSzJ6ZVhrYXUzdmN5bDNISUt1cEpmcTJU
+RUFDZWZWamowdApKVytYMzVQR1dwOS9INXpJVU5WTlZqUzdVbXM4NEl2S2hSQjg1MTJQQjlVeUhh
+Z1hZVlg1R1dwQWNWcHlmcmxSCkZJOVFkaGgrUGJrMHV5a3RkYmYvQ2RmZ0hPb2ViclR0d1Jsak0w
+b0R0WCsyQ3Y2ajB3Qks3aEQ4cFB2ZjErdXkKR3pjemlnQVUvNEt3N2VacXlkZjlCKzVSdXBSK0la
+aXBYNDF4RWlJcktSd3FpNTE3V1d6WGNqYUcyY05iZjQ1MQp4cEg1UG5WM2kxdHEwNGpNR1FVekZ3
+SURBUUFCbzRHQU1INHdIUVlEVlIwT0JCWUVGSXdYNHZzOEJpQmNTY29kCjVub1pIUk04RTQraU1F
+SUdBMVVkSXdRN01EbUFGSXdYNHZzOEJpQmNTY29kNW5vWkhSTThFNCtpb1Jha0ZEQVMKTVJBd0Rn
+WURWUVFERXdkRlFWQWdRMEV4Z2drQWd1VVYzRE10VzZzd0RBWURWUjBUQkFVd0F3RUIvekFMQmdO
+VgpIUThFQkFNQ0FRWXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmZRcU9UQTdSdjdLK2x1UTdw
+bmFzNEJZd0hFCjlHRVAvdW9odjZLT3kwVEdRRmJyUlRqRm9MVk5COUJaMXltTURaMC9USXdJVWM3
+d2k3YTh0NW1FcVlIMTUzd1cKYVdvb2lTanlMTGh1STRzTnJOQ090aXNkQnEycjJNRlh0NmgwbUFR
+WU9QdjhSOEs3L2ZnU3hHRnF6aHlObW1WTAoxcUJKbGR4MzRTcHdzVEFMUVZQYjRoR3dKelpmcjFQ
+Y3BFUXg2eE1uVGw4eEVXWkUzTXM5OXVhVXhiUXFJd1J1CkxnQU9rTkNtWTJtODlWaHphSEoxdVY4
+NUFkTS90RCtZc21sbm5qdDlMUkNlamJCaXBqSUdqT1hyZzFKUCtseFYKbXVNNHZIK1AvbWxteHNQ
+UHowZDY1YitFR21KWnBvTGtPL3RkTk52Q1l6akpwVEVXcEVzTzZOTWhLWW89Ci0tLS0tRU5EIENF
+UlRJRklDQVRFLS0tLS0K
+--{boundary}--
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
new file mode 100644
index 0000000..906bfb3
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJvZmlsZQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBi
+YXNlNjQKClBFMW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lE
+eFdaWEpFVkVRK01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJX
+VStVR1Z5VUhKdmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BG
+SlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldV
+K2RYSnVPbmRtCllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNK
+cGNIUnBiMjQ2TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFK
+VVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRB
+d01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0
+WlQ1SWIyMWxVMUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn
+SUNBZ1BFNXZaR1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn
+SUNBZ0lDQThWbUZzZFdVK1EyVnVkSFZ5ZVNCSWIzVnpaVHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn
+UEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ
+a1pSUkU0OEwwNXZaR1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbTFwTmk1amJ5NTFh
+end2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2cKSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ
+Q0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6YjNKMGFYVnRUMGs4TDA1dgpa
+R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFOalk4TDFaaGJI
+VmxQZ29nSUNBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBOFRt
+OWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpHVnUKZEdsaGJEd3ZUbTlrWlU1aGJX
+VStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStVbVZoYkcw
+OApMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbk5vWVd0bGJpNXpkR2x5Y21W
+a0xtTnZiVHd2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS
+bFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWwKVUdGemMzZHZjbVE4TDA1
+dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJs
+VG1GdApaVDVWYzJWeWJtRnRaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
+WlQ1cVlXMWxjend2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsQmgKYzNOM2IzSmtQQzlP
+YjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGxsdE9YVmFSRUYzVG5jOVBUd3ZW
+bUZzZFdVKwpDaUFnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJ
+Q0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsClBrVkJVRTFsZEdodlpEd3ZUbTlrWlU1aGJXVStD
+aUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRa
+VDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK01q
+RThMMVpoYkhWbApQZ29nSUNBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lE
+eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhPCmIyUmxUbUZ0WlQ1SmJtNWxjazFsZEdodlpE
+d3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGsxVExVTkkKUVZBdFZq
+SThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4TDA1
+dlpHVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNB
+Z0lDQWdQRTV2WkdWT1lXMWxQa1JwWjJsMFlXeERaWEowCmFXWnBZMkYwWlR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtO
+bGNuUnBabWxqWVhSbFZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
+WlQ1NE5UQTVkak04TDFaaApiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0Fn
+SUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVEWlhKMFUwaEJNalUy
+Um1sdVoyVnlVSEpwYm5ROEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSsK
+TVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1N
+V1l4WmpGbU1XWXhaakZtTVdZeApaakZtTVdZeFpqd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBsTkpUVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFRt
+OWsKWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa2xOVTBrOEwwNXZaR1ZPWVcxbFBn
+b2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1GcwpkV1UrYVcxemFUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lD
+QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnCklDQWdJQ0FnUEU1dlpH
+Vk9ZVzFsUGtWQlVGUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+K01qUTgKTDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05
+a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnUEM5T2IyUmxQZ29nSUR3dlRtOWtaVDRLUEM5
+TloyMTBWSEpsWlQ0SwoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC14
+NTA5LWNhLWNlcnQKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgpMUzB0TFMxQ1JV
+ZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTFJFTkRRV2hEWjBGM1NVSkJaMGxLUVVs
+TWJFWmtkM3BNClZuVnlUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOUWtsNFJVUkJUMEpuVGxZ
+S1FrRk5WRUl3VmtKVlEwSkVVVlJGZDBob1kwNU4KVkZsM1RWUkZlVTFVUlRGTlJFVXhWMmhqVGsx
+cVdYZE5WRUUxVFZSRk1VMUVSVEZYYWtGVFRWSkJkd3BFWjFsRVZsRlJSRVYzWkVaUgpWa0ZuVVRC
+RmVFMUpTVUpKYWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZGUmtGQlQwTkJVVGhCVFVsSlFrTm5TME5C
+VVVWQkNucHVRVkJWCmVqSTJUWE5oWlRSM2N6UXpZM3BTTkRFdlNqSlJkSEpUU1ZwVlMyMVdWWE5X
+ZFcxRVlsbEljbEJPZGxSWVMxTk5XRUZqWlhkUFVrUlIKV1ZnS1VuRjJTSFp3YmpoRGMyTkNNU3R2
+UjFoYWRraDNlR28wZWxZd1YwdHZTeko2WlZocllYVXpkbU41YkROSVNVdDFjRXBtY1RKVQpSVUZE
+WldaV2Ftb3dkQXBLVnl0WU16VlFSMWR3T1M5SU5YcEpWVTVXVGxacVV6ZFZiWE00TkVsMlMyaFNR
+amcxTVRKUVFqbFZlVWhoCloxaFpWbGcxUjFkd1FXTldjSGxtY214U0NrWkpPVkZrYUdnclVHSnJN
+SFY1YTNSa1ltWXZRMlJtWjBoUGIyVmljbFIwZDFKc2FrMHcKYjBSMFdDc3lRM1kyYWpCM1FrczNh
+RVE0Y0ZCMlpqRXJkWGtLUjNwamVtbG5RVlV2TkV0M04yVmFjWGxrWmpsQ0t6VlNkWEJTSzBsYQph
+WEJZTkRGNFJXbEpja3RTZDNGcE5URTNWMWQ2V0dOcVlVY3lZMDVpWmpRMU1RcDRjRWcxVUc1V00y
+a3hkSEV3TkdwTlIxRlZla1ozClNVUkJVVUZDYnpSSFFVMUlOSGRJVVZsRVZsSXdUMEpDV1VWR1NY
+ZFlOSFp6T0VKcFFtTlRZMjlrQ2pWdWIxcElVazA0UlRRcmFVMUYKU1VkQk1WVmtTWGRSTjAxRWJV
+RkdTWGRZTkhaek9FSnBRbU5UWTI5a05XNXZXa2hTVFRoRk5DdHBiMUpoYTBaRVFWTUtUVkpCZDBS
+bgpXVVJXVVZGRVJYZGtSbEZXUVdkUk1FVjRaMmRyUVdkMVZWWXpSRTEwVnpaemQwUkJXVVJXVWpC
+VVFrRlZkMEYzUlVJdmVrRk1RbWRPClZncElVVGhGUWtGTlEwRlJXWGRFVVZsS1MyOWFTV2gyWTA1
+QlVVVk1RbEZCUkdkblJVSkJSbVpSY1U5VVFUZFNkamRMSzJ4MVVUZHcKYm1Gek5FSlpkMGhGQ2ps
+SFJWQXZkVzlvZGpaTFQza3dWRWRSUm1KeVVsUnFSbTlNVms1Q09VSmFNWGx0VFVSYU1DOVVTWGRK
+VldNMwpkMmszWVRoME5XMUZjVmxJTVRVemQxY0tZVmR2YjJsVGFubE1UR2gxU1RSelRuSk9RMDkw
+YVhOa1FuRXljakpOUmxoME5tZ3diVUZSCldVOVFkamhTT0VzM0wyWm5VM2hIUm5GNmFIbE9iVzFX
+VEFveGNVSktiR1I0TXpSVGNIZHpWRUZNVVZaUVlqUm9SM2RLZWxwbWNqRlEKWTNCRlVYZzJlRTF1
+Vkd3NGVFVlhXa1V6VFhNNU9YVmhWWGhpVVhGSmQxSjFDa3huUVU5clRrTnRXVEp0T0RsV2FIcGhT
+RW94ZFZZNApOVUZrVFM5MFJDdFpjMjFzYm01cWREbE1Va05sYW1KQ2FYQnFTVWRxVDFoeVp6RktV
+Q3RzZUZZS2JYVk5OSFpJSzFBdmJXeHRlSE5RClVIb3daRFkxWWl0RlIyMUtXbkJ2VEd0UEwzUmtU
+azUyUTFsNmFrcHdWRVZYY0VWelR6Wk9UV2hMV1c4OUNpMHRMUzB0UlU1RUlFTkYKVWxSSlJrbERR
+VlJGTFMwdExTMEsKLS17Ym91bmRhcnl9LS0K
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
new file mode 100644
index 0000000..3fa97d1
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
new file mode 100644
index 0000000..975f8e5
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTMyCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64
new file mode 100644
index 0000000..833c527
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64
@@ -0,0 +1,31 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNl
+NjQKCkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERa
+MEYzU1VKQlowbEtRVWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5R
+a2w0UlVSQlQwSm5UbFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVS
+VEZOUkVVeFYyaGpUazFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJF
+VjNaRVpSClZrRm5VVEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVU
+aEJUVWxKUWtOblMwTkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRI
+SlRTVnBWUzIxV1ZYTldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNI
+WndiamhEYzJOQ01TdHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0
+MWNFcG1jVEpVClJVRkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpk
+VmJYTTRORWwyUzJoU1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2ta
+Sk9WRmthR2dyVUdKck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0Nz
+eVEzWTJhakIzUWtzM2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxD
+S3pWU2RYQlNLMGxhCmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalEx
+TVFwNGNFZzFVRzVXTTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxF
+VmxJd1QwSkNXVVZHU1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRC
+TVZWa1NYZFJOMDFFYlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhh
+MFpFUVZNS1RWSkJkMFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBW
+elp6ZDBSQldVUldVakJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVV
+VmxLUzI5YVNXaDJZMDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpi
+bUZ6TkVKWmQwaEZDamxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1Y
+bHRUVVJhTUM5VVNYZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRH
+aDFTVFJ6VG5KT1EwOTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUz
+aEhSbkY2YUhsT2JXMVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pG
+UQpZM0JGVVhnMmVFMXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtO
+dFdUSnRPRGxXYUhwaFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhC
+cVNVZHFUMWh5WnpGS1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIx
+S1duQnZUR3RQTDNSa1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVF
+SUVORgpVbFJKUmtsRFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/README.txt b/wifi/tests/assets/hsr1/README.txt
new file mode 100644
index 0000000..d1f8384
--- /dev/null
+++ b/wifi/tests/assets/hsr1/README.txt
@@ -0,0 +1,5 @@
+HSR1ProfileWithCACert.conf - unencoded installation file that contains a Passpoint profile and a CA Certificate
+HSR1ProfileWithCACert.base64 - base64 encoded of the data contained in HSR1ProfileWithCAWith.conf
+HSR1ProfileWithNonBase64Part.base64 - base64 encoded installation file that contains a part of non-base64 encoding type
+HSR1ProfileWithMissingBoundary.base64 - base64 encoded installation file with missing end-boundary in the MIME data
+HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type.
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
new file mode 100644
index 0000000..53d38ad
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -0,0 +1,80 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml
new file mode 100644
index 0000000..e13eb2a
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml
@@ -0,0 +1,95 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml
new file mode 100644
index 0000000..8719ffa
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml
@@ -0,0 +1,81 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml
new file mode 100644
index 0000000..c761237
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml
@@ -0,0 +1,80 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml
new file mode 100644
index 0000000..6b807af
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml
@@ -0,0 +1,84 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+          <Node>
+            <NodeName>InvalidNode</NodeName>
+            <Value>Test</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml
new file mode 100644
index 0000000..ed06b47
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml
@@ -0,0 +1,79 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml
new file mode 100644
index 0000000..f7e35dd
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml
@@ -0,0 +1,79 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256FingerPrint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/README.txt b/wifi/tests/assets/pps/README.txt
new file mode 100644
index 0000000..369c0a9
--- /dev/null
+++ b/wifi/tests/assets/pps/README.txt
@@ -0,0 +1,7 @@
+PerProviderSubscription.xml - valid PPS XML file
+PerProviderSubscription_DuplicateHomeSP.xml - containing multiple HomeSP node
+PerProviderSubscription_DuplicateValue.xml - FriendlyName node contains multiple Value
+PerProviderSubscription_MissingValue.xml - FriendlyName node is missing Value
+PerProviderSubscription_MissingName.xml - HomeSP node is missing NodeName
+PerProviderSubscription_InvalidNode.xml - FQDN node contains both Value and a child node
+PerProviderSubscription_InvalidName.xml - FriendlyName node have a typo in its name
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java
new file mode 100644
index 0000000..6095929
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.FakeKeys;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.ConfigBuilder}.
+ */
+@SmallTest
+public class ConfigBuilderTest {
+    /**
+     * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a
+     * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}.
+     */
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT =
+            "assets/hsr1/HSR1ProfileWithCACert.base64";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA =
+            "assets/hsr1/HSR1ProfileWithCACert.conf";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART =
+            "assets/hsr1/HSR1ProfileWithNonBase64Part.base64";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY =
+            "assets/hsr1/HSR1ProfileWithMissingBoundary.base64";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE =
+            "assets/hsr1/HSR1ProfileWithInvalidContentType.base64";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE =
+            "assets/hsr1/HSR1ProfileWithoutProfile.base64";
+
+    /**
+     * Read the content of the given resource file into a String.
+     *
+     * @param filename String name of the file
+     * @return String
+     * @throws IOException
+     */
+    private String loadResourceFile(String filename) throws IOException {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append("\n");
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+     * XML file {@link #PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT}.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private PasspointConfiguration generateConfigurationFromProfile() {
+        PasspointConfiguration config = new PasspointConfiguration();
+
+        // HomeSP configuration.
+        config.homeSp = new HomeSP();
+        config.homeSp.friendlyName = "Century House";
+        config.homeSp.fqdn = "mi6.co.uk";
+        config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L};
+
+        // Credential configuration.
+        config.credential = new Credential();
+        config.credential.realm = "shaken.stirred.com";
+        config.credential.userCredential = new Credential.UserCredential();
+        config.credential.userCredential.username = "james";
+        config.credential.userCredential.password = "Ym9uZDAwNw==";
+        config.credential.userCredential.eapType = 21;
+        config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+        config.credential.certCredential = new Credential.CertificateCredential();
+        config.credential.certCredential.certType = "x509v3";
+        config.credential.certCredential.certSha256FingerPrint = new byte[32];
+        Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f);
+        config.credential.simCredential = new Credential.SimCredential();
+        config.credential.simCredential.imsi = "imsi";
+        config.credential.simCredential.eapType = 24;
+        config.credential.caCertificate = FakeKeys.CA_CERT0;
+        return config;
+    }
+
+    /**
+     * Verify a valid installation file is parsed successfully with the matching contents.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFile() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+        PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+        PasspointConfiguration actualConfig =
+                ConfigBuilder.buildPasspointConfig(
+                        "application/x-wifi-config", configStr.getBytes());
+        assertTrue(actualConfig.equals(expectedConfig));
+    }
+
+    /**
+     * Verify that parsing an installation file with invalid MIME type will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithInvalidMimeType() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/wifi-config", configStr.getBytes()));
+    }
+
+    /**
+     * Verify that parsing an un-encoded installation file will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithUnencodedData() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/x-wifi-config", configStr.getBytes()));
+    }
+
+    /**
+     * Verify that parsing an installation file that contains a non-base64 part will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithInvalidPart() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/x-wifi-config", configStr.getBytes()));
+    }
+
+    /**
+     * Verify that parsing an installation file that contains a missing boundary string will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithMissingBoundary() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/x-wifi-config", configStr.getBytes()));
+    }
+
+    /**
+     * Verify that parsing an installation file that contains a MIME part with an invalid content
+     * type will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithInvalidContentType() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/x-wifi-config", configStr.getBytes()));
+    }
+
+    /**
+     * Verify that parsing an installation file that doesn't contain a Passpoint profile will fail.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseConfigFileWithoutPasspointProfile() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE);
+        assertNull(ConfigBuilder.buildPasspointConfig(
+                "application/x-wifi-config", configStr.getBytes()));
+    }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
new file mode 100644
index 0000000..b4a3acf
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
+ */
+@SmallTest
+public class PasspointConfigurationTest {
+
+    private static HomeSP createHomeSp() {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        return homeSp;
+    }
+
+    private static Credential createCredential() {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = null;
+        cred.certCredential = null;
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_SIM;
+        cred.caCertificate = null;
+        cred.clientCertificateChain = null;
+        cred.clientPrivateKey = null;
+        return cred;
+    }
+
+    private static void verifyParcel(PasspointConfiguration writeConfig) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeConfig.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        PasspointConfiguration readConfig =
+                PasspointConfiguration.CREATOR.createFromParcel(parcel);
+        assertTrue(readConfig.equals(writeConfig));
+    }
+
+    /**
+     * Verify parcel read/write for a default configuration.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithDefault() throws Exception {
+        verifyParcel(new PasspointConfiguration());
+    }
+
+    /**
+     * Verify parcel read/write for a configuration that contained both HomeSP and Credential.
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithHomeSPAndCredential() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        config.credential = createCredential();
+        verifyParcel(config);
+    }
+
+    /**
+     * Verify parcel read/write for a configuration that contained only HomeSP.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithHomeSPOnly() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        verifyParcel(config);
+    }
+
+    /**
+     * Verify parcel read/write for a configuration that contained only Credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithCredentialOnly() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.credential = createCredential();
+        verifyParcel(config);
+    }
+
+    /**
+     * Verify that a default/empty configuration is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateDefaultConfig() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        assertFalse(config.validate());
+    }
+
+    /**
+     * Verify that a configuration without Credential is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateConfigWithoutCredential() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        assertFalse(config.validate());
+    }
+
+    /**
+     * Verify that a a configuration without HomeSP is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateConfigWithoutHomeSp() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.credential = createCredential();
+        assertFalse(config.validate());
+    }
+
+    /**
+     * Verify a valid configuration.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateValidConfig() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        config.credential = createCredential();
+        assertTrue(config.validate());
+    }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
new file mode 100644
index 0000000..10b0267
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2.omadm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.omadm.PPSMOParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}.
+ */
+@SmallTest
+public class PPSMOParserTest {
+    private static final String VALID_PPS_MO_XML_FILE = "assets/pps/PerProviderSubscription.xml";
+    private static final String PPS_MO_XML_FILE_DUPLICATE_HOMESP =
+            "assets/pps/PerProviderSubscription_DuplicateHomeSP.xml";
+    private static final String PPS_MO_XML_FILE_DUPLICATE_VALUE =
+            "assets/pps/PerProviderSubscription_DuplicateValue.xml";
+    private static final String PPS_MO_XML_FILE_MISSING_VALUE =
+            "assets/pps/PerProviderSubscription_MissingValue.xml";
+    private static final String PPS_MO_XML_FILE_MISSING_NAME =
+            "assets/pps/PerProviderSubscription_MissingName.xml";
+    private static final String PPS_MO_XML_FILE_INVALID_NODE =
+            "assets/pps/PerProviderSubscription_InvalidNode.xml";
+    private static final String PPS_MO_XML_FILE_INVALID_NAME =
+            "assets/pps/PerProviderSubscription_InvalidName.xml";
+
+    /**
+     * Read the content of the given resource file into a String.
+     *
+     * @param filename String name of the file
+     * @return String
+     * @throws IOException
+     */
+    private String loadResourceFile(String filename) throws IOException {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append("\n");
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+     * XML file {@link #VALID_PPS_MO_XML_FILE}.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private PasspointConfiguration generateConfigurationFromPPSMOTree() {
+        PasspointConfiguration config = new PasspointConfiguration();
+
+        // HomeSP configuration.
+        config.homeSp = new HomeSP();
+        config.homeSp.friendlyName = "Century House";
+        config.homeSp.fqdn = "mi6.co.uk";
+        config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L};
+
+        // Credential configuration.
+        config.credential = new Credential();
+        config.credential.realm = "shaken.stirred.com";
+        config.credential.userCredential = new Credential.UserCredential();
+        config.credential.userCredential.username = "james";
+        config.credential.userCredential.password = "Ym9uZDAwNw==";
+        config.credential.userCredential.eapType = 21;
+        config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+        config.credential.certCredential = new Credential.CertificateCredential();
+        config.credential.certCredential.certType = "x509v3";
+        config.credential.certCredential.certSha256FingerPrint = new byte[32];
+        Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f);
+        config.credential.simCredential = new Credential.SimCredential();
+        config.credential.simCredential.imsi = "imsi";
+        config.credential.simCredential.eapType = 24;
+        return config;
+    }
+
+    /**
+     * Parse and verify all supported fields under PPS MO tree (currently only fields under
+     * HomeSP and Credential subtree).
+     *
+     * @throws Exception
+     */
+    @Test
+    public void parseValidPPSMOTree() throws Exception {
+        String ppsMoTree = loadResourceFile(VALID_PPS_MO_XML_FILE);
+        PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree();
+        PasspointConfiguration actualConfig = PPSMOParser.parseMOText(ppsMoTree);
+        assertTrue(actualConfig.equals(expectedConfig));
+    }
+
+    @Test
+    public void parseNullPPSMOTree() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(null));
+    }
+
+    @Test
+    public void parseEmptyPPSMOTree() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(new String()));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithDuplicateHomeSP() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_HOMESP)));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithDuplicateValue() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_VALUE)));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithMissingValue() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_MISSING_VALUE)));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithMissingName() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_MISSING_NAME)));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithInvalidNode() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_INVALID_NODE)));
+    }
+
+    @Test
+    public void parsePPSMOTreeWithInvalidName() throws Exception {
+        assertEquals(null, PPSMOParser.parseMOText(
+                loadResourceFile(PPS_MO_XML_FILE_INVALID_NAME)));
+    }
+}
+
+
+
+
+
+
+
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
new file mode 100644
index 0000000..c2dcec6
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2.omadm;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.omadm.XMLNode;
+import android.net.wifi.hotspot2.omadm.XMLParser;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.omadm.XMLParser}.
+ */
+@SmallTest
+public class XMLParserTest {
+    XMLParser mParser;
+
+    private static XMLNode createNode(XMLNode parent, String tag, String text) {
+        XMLNode node = new XMLNode(parent, tag);
+        node.addText(text);
+        if (parent != null)
+            parent.addChild(node);
+        node.close();
+        return node;
+    }
+
+    /**
+     * Setup before tests.
+     */
+    @Before
+    public void setUp() throws Exception {
+        mParser = new XMLParser();
+    }
+
+    @Test(expected = IOException.class)
+    public void parseNullXML() throws Exception {
+        mParser.parse(null);
+    }
+
+    @Test(expected = IOException.class)
+    public void parseEmptyXML() throws Exception {
+        mParser.parse(new String());
+    }
+
+    @Test(expected = SAXException.class)
+    public void parseMalformedXML() throws Exception {
+        String malformedXmlTree = "<root><child1>test1</child2></root>";
+        mParser.parse(malformedXmlTree);
+    }
+
+    @Test
+    public void parseValidXMLTree() throws Exception {
+        String xmlTree = "<root><child1>test1</child1><child2>test2</child2></root>";
+
+        // Construct the expected XML tree.
+        XMLNode expectedRoot = createNode(null, "root", "");
+        createNode(expectedRoot, "child1", "test1");
+        createNode(expectedRoot, "child2", "test2");
+
+        XMLNode actualRoot = mParser.parse(xmlTree);
+        assertTrue(actualRoot.equals(expectedRoot));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
new file mode 100644
index 0000000..223aa52
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.FakeKeys;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.CredentialTest}.
+ */
+@SmallTest
+public class CredentialTest {
+    private static Credential createCredential(Credential.UserCredential userCred,
+                                               Credential.CertificateCredential certCred,
+                                               Credential.SimCredential simCred,
+                                               X509Certificate caCert,
+                                               X509Certificate[] clientCertificateChain,
+                                               PrivateKey clientPrivateKey) {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = userCred;
+        cred.certCredential = certCred;
+        cred.simCredential = simCred;
+        cred.caCertificate = caCert;
+        cred.clientCertificateChain = clientCertificateChain;
+        cred.clientPrivateKey = clientPrivateKey;
+        return cred;
+    }
+
+    private static Credential createCredentialWithCertificateCredential() {
+        Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+        certCred.certType = "x509v3";
+        certCred.certSha256FingerPrint = new byte[32];
+        return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
+                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+    }
+
+    private static Credential createCredentialWithSimCredential() {
+        Credential.SimCredential simCred = new Credential.SimCredential();
+        simCred.imsi = "1234*";
+        simCred.eapType = EAPConstants.EAP_SIM;
+        return createCredential(null, null, simCred, null, null, null);
+    }
+
+    private static Credential createCredentialWithUserCredential() {
+        Credential.UserCredential userCred = new Credential.UserCredential();
+        userCred.username = "username";
+        userCred.password = "password";
+        userCred.eapType = EAPConstants.EAP_TTLS;
+        userCred.nonEapInnerMethod = "MS-CHAP";
+        return createCredential(userCred, null, null, FakeKeys.CA_CERT0,
+                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+    }
+
+    private static void verifyParcel(Credential writeCred) {
+        Parcel parcel = Parcel.obtain();
+        writeCred.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        Credential readCred = Credential.CREATOR.createFromParcel(parcel);
+        assertTrue(readCred.equals(writeCred));
+    }
+
+    /**
+     * Verify parcel read/write for a default/empty credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithDefault() throws Exception {
+        verifyParcel(new Credential());
+    }
+
+    /**
+     * Verify parcel read/write for a certificate credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithCertificateCredential() throws Exception {
+        verifyParcel(createCredentialWithCertificateCredential());
+    }
+
+    /**
+     * Verify parcel read/write for a SIM credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithSimCredential() throws Exception {
+        verifyParcel(createCredentialWithSimCredential());
+    }
+
+    /**
+     * Verify parcel read/write for an user credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithUserCredential() throws Exception {
+        verifyParcel(createCredentialWithUserCredential());
+    }
+
+    /**
+     * Verify a valid user credential.
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredential() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertTrue(cred.validate());
+    }
+
+    /**
+     * Verify that an user credential without CA Certificate is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithoutCaCert() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that an user credential with EAP type other than EAP-TTLS is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithEapTls() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertFalse(cred.validate());
+    }
+
+
+    /**
+     * Verify that an user credential without realm is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithoutRealm() throws Exception {
+        Credential cred = new Credential();
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that an user credential without username is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithoutUsername() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that an user credential without password is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithoutPassword() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that an user credential without auth methoh (non-EAP inner method) is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUserCredentialWithoutAuthMethod() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify a certificate credential. CA Certificate, client certificate chain,
+     * and client private key are all required.  Also the digest for client
+     * certificate must match the fingerprint specified in the certificate credential.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateCertCredential() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup certificate credential.
+        cred.certCredential = new Credential.CertificateCredential();
+        cred.certCredential.certType = "x509v3";
+        cred.certCredential.certSha256FingerPrint =
+                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+        // Setup certificates and private key.
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+        cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+        assertTrue(cred.validate());
+    }
+
+    /**
+     * Verify that an certificate credential without CA Certificate is invalid.
+     *
+     * @throws Exception
+     */
+    public void validateCertCredentialWithoutCaCert() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup certificate credential.
+        cred.certCredential = new Credential.CertificateCredential();
+        cred.certCredential.certType = "x509v3";
+        cred.certCredential.certSha256FingerPrint =
+                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+        // Setup certificates and private key.
+        cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+        cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a certificate credential without client certificate chain is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateCertCredentialWithoutClientCertChain() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup certificate credential.
+        cred.certCredential = new Credential.CertificateCredential();
+        cred.certCredential.certType = "x509v3";
+        cred.certCredential.certSha256FingerPrint =
+                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+        // Setup certificates and private key.
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a certificate credential without client private key is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateCertCredentialWithoutClientPrivateKey() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup certificate credential.
+        cred.certCredential = new Credential.CertificateCredential();
+        cred.certCredential.certType = "x509v3";
+        cred.certCredential.certSha256FingerPrint =
+                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+        // Setup certificates and private key.
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a certificate credential with mismatch client certificate fingerprint
+     * is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateCertCredentialWithMismatchFingerprint() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup certificate credential.
+        cred.certCredential = new Credential.CertificateCredential();
+        cred.certCredential.certType = "x509v3";
+        cred.certCredential.certSha256FingerPrint = new byte[32];
+        Arrays.fill(cred.certCredential.certSha256FingerPrint, (byte)0);
+        // Setup certificates and private key.
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+        cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify a SIM credential using EAP-SIM.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithEapSim() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_SIM;
+        assertTrue(cred.validate());
+    }
+
+    /**
+     * Verify a SIM credential using EAP-AKA.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithEapAka() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_AKA;
+        assertTrue(cred.validate());
+    }
+
+    /**
+     * Verify a SIM credential using EAP-AKA-PRIME.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithEapAkaPrime() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_AKA_PRIME;
+        assertTrue(cred.validate());
+    }
+
+    /**
+     * Verify that a SIM credential without IMSI is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithoutIMSI() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.eapType = EAPConstants.EAP_SIM;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a SIM credential with an invalid IMSI is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithInvalidIMSI() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "dummy";
+        cred.simCredential.eapType = EAPConstants.EAP_SIM;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a SIM credential with invalid EAP type is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSimCredentialWithEapTls() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_TLS;
+        assertFalse(cred.validate());
+    }
+
+    /**
+     * Verify that a credential contained both an user and a SIM credential is invalid.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateCredentialWithUserAndSimCredential() throws Exception {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        // Setup user credential with EAP-TTLS.
+        cred.userCredential = new Credential.UserCredential();
+        cred.userCredential.username = "username";
+        cred.userCredential.password = "password";
+        cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+        cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+        cred.caCertificate = FakeKeys.CA_CERT0;
+        // Setup SIM credential.
+        cred.simCredential = new Credential.SimCredential();
+        cred.simCredential.imsi = "1234*";
+        cred.simCredential.eapType = EAPConstants.EAP_SIM;
+        assertFalse(cred.validate());
+    }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
new file mode 100644
index 0000000..fff1477
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 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 android.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSP}.
+ */
+@SmallTest
+public class HomeSPTest {
+    private static HomeSP createHomeSp() {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        return homeSp;
+    }
+
+    private static void verifyParcel(HomeSP writeHomeSp) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeHomeSp.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        HomeSP readHomeSp = HomeSP.CREATOR.createFromParcel(parcel);
+        assertTrue(readHomeSp.equals(writeHomeSp));
+    }
+
+    /**
+     * Verify parcel read/write for an empty HomeSP.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithEmptyHomeSP() throws Exception {
+        verifyParcel(new HomeSP());
+    }
+
+    /**
+     * Verify parcel read/write for a valid HomeSP.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithValidHomeSP() throws Exception {
+        verifyParcel(createHomeSp());
+    }
+
+    /**
+     * Verify that a HomeSP is valid when both FQDN and Friendly Name
+     * are provided.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateValidHomeSP() throws Exception {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        assertTrue(homeSp.validate());
+    }
+
+    /**
+     * Verify that a HomeSP is not valid when FQDN is not provided
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithoutFqdn() throws Exception {
+        HomeSP homeSp = new HomeSP();
+        homeSp.friendlyName = "friendly name";
+        assertFalse(homeSp.validate());
+    }
+
+    /**
+     * Verify that a HomeSP is not valid when Friendly Name is not provided
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithoutFriendlyName() throws Exception {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        assertFalse(homeSp.validate());
+    }
+
+    /**
+     * Verify that a HomeSP is valid when the optional Roaming Consortium OIs are
+     * provided.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithRoamingConsoritums() throws Exception {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        assertTrue(homeSp.validate());
+    }
+}