Merge "Fix idmap leak in zygote process"
diff --git a/Android.mk b/Android.mk
index ada4ac5..53d4b3e 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 \
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 073196c..11c2735 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -786,19 +786,13 @@
     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);
+            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);
@@ -1519,6 +1513,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.
@@ -1949,6 +1973,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;
         }
@@ -2021,6 +2048,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/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/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 8c98536..96a1ae8 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);
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/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/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/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/res/res/values/config.xml b/core/res/res/values/config.xml
index 0201111..a8be8d3 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>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2345885..6f2822b 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" />
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/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 7875cb8..2a5b194 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -221,6 +221,7 @@
                                 mBluetoothLock.readLock().lock();
                                 if (mBluetooth != null) {
                                     mBluetooth.onBrEdrDown();
+                                    mEnable = false;
                                     mEnableExternal = false;
                                 }
                             } catch (RemoteException e) {
@@ -436,14 +437,16 @@
 
     class ClientDeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
-            if (DBG) Slog.d(TAG, "Binder is dead -  unregister Ble App");
+            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) {
+                    if (mBluetooth != null &&
+                        mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                        mEnable = false;
                         mBluetooth.onBrEdrDown();
                     }
                 } catch (RemoteException e) {
@@ -460,6 +463,9 @@
 
     @Override
     public boolean isBleScanAlwaysAvailable() {
+        if (isAirplaneModeOn() && !mEnable) {
+            return false;
+        }
         try {
             return (Settings.Global.getInt(mContentResolver,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0;
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4d7c304..66d704b 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;
@@ -315,122 +536,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 +1047,7 @@
                 mAdapter.putExtras(id, extras);
             }
         }
-        
+
         public void onExtrasRemoved(Connection c, List<String> keys) {
             String id = mIdByConnection.get(c);
             if (id != null) {
@@ -1271,15 +1623,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 +1840,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 1e5769f..446bbbb 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -184,6 +184,10 @@
         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);
     }
@@ -193,14 +197,14 @@
         getSessionManager().startSession(info, shortMethodName, callerIdentification);
     }
 
-    public static void startExternalSession(Session.Info sessionInfo, String shortMethodName) {
-        getSessionManager().startExternalSession(sessionInfo, shortMethodName);
-    }
-
     public static Session createSubsession() {
         return getSessionManager().createSubsession();
     }
 
+    public static Session.Info getExternalSession() {
+        return getSessionManager().getExternalSession();
+    }
+
     public static void cancelSubsession(Session subsession) {
         getSessionManager().cancelSubsession(subsession);
     }
diff --git a/telecomm/java/android/telecom/Logging/Session.aidl b/telecomm/java/android/telecom/Logging/Session.aidl
new file mode 100644
index 0000000..68961b6
--- /dev/null
+++ b/telecomm/java/android/telecom/Logging/Session.aidl
@@ -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
+ */
+
+package android.telecom.Logging;
+
+/**
+ * {@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 093c0f9..3a7b8c0 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -281,7 +281,15 @@
             parentSession.getFullMethodPath(sb);
             sb.append(SUBSESSION_SEPARATION_CHAR);
         }
-        sb.append(mShortMethodName);
+        // Encapsulate the external session's method name so it is obvious what part of the session
+        // is external.
+        if (isExternal()) {
+            sb.append("(");
+            sb.append(mShortMethodName);
+            sb.append(")");
+        } else {
+            sb.append(mShortMethodName);
+        }
 
         if(isSessionStarted) {
             // Cache this value so that we do not have to do this work next time!
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 173e7ee..8ced7f81 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -229,6 +229,23 @@
     }
 
     /**
+     * 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 mSessionCleanupHandler forcefully later.
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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4dab2bb..79130e4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1118,7 +1118,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 +1134,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;