Merge "Merge kwd to master"
diff --git a/Android.mk b/Android.mk
index 7a46a1f..58bc2c7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -98,6 +98,8 @@
 	core/java/android/app/maintenance/IIdleService.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
+	core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
+	core/java/android/bluetooth/IBluetoothAvrcpController.aidl \
 	core/java/android/bluetooth/IBluetoothCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl \
@@ -110,6 +112,7 @@
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothMap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
+	core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
 	core/java/android/bluetooth/IBluetoothGatt.aidl \
 	core/java/android/bluetooth/IBluetoothGattCallback.aidl \
 	core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index a2d62b0..941eb5b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35001,7 +35001,8 @@
 
 package android.webkit {
 
-  public abstract interface ClientCertRequest {
+  public abstract class ClientCertRequest {
+    ctor public ClientCertRequest();
     method public abstract void cancel();
     method public abstract java.lang.String getHost();
     method public abstract java.lang.String[] getKeyTypes();
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 7b709ac..5175490 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -90,6 +90,11 @@
     public static final String ACTION_PLAYING_STATE_CHANGED =
         "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
 
+    /** @hide */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
+
     /**
      * A2DP sink device is streaming music. This state can be one of
      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
new file mode 100644
index 0000000..2e27345
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth A2DP Sink
+ * profile.
+ *
+ *<p>BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothA2dpSink proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothA2dpSink implements BluetoothProfile {
+    private static final String TAG = "BluetoothA2dpSink";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent used to broadcast the change in connection state of the A2DP Sink
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Playing state of the A2DP Sink
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    public static final String ACTION_PLAYING_STATE_CHANGED =
+        "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED";
+
+    /**
+     * A2DP sink device is streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_PLAYING   =  10;
+
+    /**
+     * A2DP sink device is NOT streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_NOT_PLAYING   =  11;
+
+    /**
+     * Intent used to broadcast the change in the Playing state of the A2DP Sink
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device. </li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    public static final String ACTION_AUDIO_CONFIG_CHANGED =
+        "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED";
+
+    /**
+     * Extra for the {@link #ACTION_AUDIO_CONFIG_CHANGED} intent.
+     *
+     * This extra represents the current audio configuration of the A2DP source device.
+     * {@see BluetoothAudioConfig}
+     */
+    public static final String EXTRA_AUDIO_CONFIG
+            = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG";
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private IBluetoothA2dpSink mService;
+    private BluetoothAdapter mAdapter;
+
+    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 BluetoothA2dp proxy object for interacting with the local
+     * Bluetooth A2DP service.
+     *
+     */
+    /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) {
+        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(IBluetoothA2dpSink.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 A2DP Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    /*package*/ void close() {
+        mServiceListener = null;
+        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);
+                }
+            }
+        }
+    }
+
+    public void finalize() {
+        close();
+    }
+    /**
+     * Initiate connection to a profile of the remote bluetooth device.
+     *
+     * <p> Currently, the system supports only 1 connection to the
+     * A2DP profile. The API will automatically disconnect connected
+     * devices before connecting.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (VDBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (VDBG) log("getState(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Get the current audio configuration for the A2DP source device,
+     * or null if the device has no audio configuration
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param device Remote bluetooth device.
+     * @return audio configuration for the device, or null
+     *
+     * {@see BluetoothAudioConfig}
+     */
+          public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
+        if (VDBG) log("getAudioConfig(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getAudioConfig(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return null;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return null;
+    }
+
+    /**
+     * Helper for converting a state to a string.
+     *
+     * For debug use only - strings are not internationalized.
+     * @hide
+     */
+    public static String stateToString(int state) {
+        switch (state) {
+        case STATE_DISCONNECTED:
+            return "disconnected";
+        case STATE_CONNECTING:
+            return "connecting";
+        case STATE_CONNECTED:
+            return "connected";
+        case STATE_DISCONNECTING:
+            return "disconnecting";
+        case STATE_PLAYING:
+            return "playing";
+        case STATE_NOT_PLAYING:
+          return "not playing";
+        default:
+            return "<unknown state " + state + ">";
+        }
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            mService = IBluetoothA2dpSink.Stub.asInterface(service);
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK,
+                        BluetoothA2dpSink.this);
+            }
+        }
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private static void log(String msg) {
+      Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 42c2aeb..ba42f51b 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1390,6 +1390,12 @@
         } else if (profile == BluetoothProfile.A2DP) {
             BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.A2DP_SINK) {
+            BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
+            return true;
+        } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+            BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
+            return true;
         } else if (profile == BluetoothProfile.INPUT_DEVICE) {
             BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener);
             return true;
@@ -1402,6 +1408,9 @@
         } else if (profile == BluetoothProfile.MAP) {
             BluetoothMap map = new BluetoothMap(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
+            BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -1430,6 +1439,14 @@
                 BluetoothA2dp a2dp = (BluetoothA2dp)proxy;
                 a2dp.close();
                 break;
+            case BluetoothProfile.A2DP_SINK:
+                BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink)proxy;
+                a2dpSink.close();
+                break;
+            case BluetoothProfile.AVRCP_CONTROLLER:
+                BluetoothAvrcpController avrcp = (BluetoothAvrcpController)proxy;
+                avrcp.close();
+                break;
             case BluetoothProfile.INPUT_DEVICE:
                 BluetoothInputDevice iDev = (BluetoothInputDevice)proxy;
                 iDev.close();
@@ -1454,6 +1471,10 @@
                 BluetoothMap map = (BluetoothMap)proxy;
                 map.close();
                 break;
+            case BluetoothProfile.HEADSET_CLIENT:
+                BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy;
+                headsetClient.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.aidl b/core/java/android/bluetooth/BluetoothAudioConfig.aidl
new file mode 100644
index 0000000..63be5cf
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAudioConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 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;
+
+parcelable BluetoothAudioConfig;
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java
new file mode 100644
index 0000000..03176b9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAudioConfig.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the audio configuration for a Bluetooth A2DP source device.
+ *
+ * {@see BluetoothA2dpSink}
+ *
+ * {@hide}
+ */
+public final class BluetoothAudioConfig implements Parcelable {
+
+    private final int mSampleRate;
+    private final int mChannelConfig;
+    private final int mAudioFormat;
+
+    public BluetoothAudioConfig(int sampleRate, int channelConfig, int audioFormat) {
+        mSampleRate = sampleRate;
+        mChannelConfig = channelConfig;
+        mAudioFormat = audioFormat;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothAudioConfig) {
+            BluetoothAudioConfig bac = (BluetoothAudioConfig)o;
+            return (bac.mSampleRate == mSampleRate &&
+                    bac.mChannelConfig == mChannelConfig &&
+                    bac.mAudioFormat == mAudioFormat);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSampleRate | (mChannelConfig << 24) | (mAudioFormat << 28);
+    }
+
+    @Override
+    public String toString() {
+        return "{mSampleRate:" + mSampleRate + ",mChannelConfig:" + mChannelConfig
+                + ",mAudioFormat:" + mAudioFormat + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothAudioConfig> CREATOR =
+            new Parcelable.Creator<BluetoothAudioConfig>() {
+        public BluetoothAudioConfig createFromParcel(Parcel in) {
+            int sampleRate = in.readInt();
+            int channelConfig = in.readInt();
+            int audioFormat = in.readInt();
+            return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat);
+        }
+        public BluetoothAudioConfig[] newArray(int size) {
+            return new BluetoothAudioConfig[size];
+        }
+    };
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mSampleRate);
+        out.writeInt(mChannelConfig);
+        out.writeInt(mAudioFormat);
+    }
+
+    /**
+     * Returns the sample rate in samples per second
+     * @return sample rate
+     */
+    public int getSampleRate() {
+        return mSampleRate;
+    }
+
+    /**
+     * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO}
+     * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO})
+     * @return channel configuration
+     */
+    public int getChannelConfig() {
+        return mChannelConfig;
+    }
+
+    /**
+     * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT}
+     * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT}
+     * @return audio format
+     */
+    public int getAudioFormat() {
+        return mAudioFormat;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothAvrcp.java b/core/java/android/bluetooth/BluetoothAvrcp.java
new file mode 100644
index 0000000..44fe1b7
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAvrcp.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+/**
+ * This class contains constants for Bluetooth AVRCP profile.
+ *
+ * {@hide}
+ */
+public final class BluetoothAvrcp {
+
+    /*
+     * State flags for Passthrough commands
+    */
+    public static final int PASSTHROUGH_STATE_PRESS    = 0;
+    public static final int PASSTHROUGH_STATE_RELEASE  = 1;
+
+    /*
+     * Operation IDs for Passthrough commands
+    */
+    public static final int PASSTHROUGH_ID_SELECT      = 0x00;    /* select */
+    public static final int PASSTHROUGH_ID_UP          = 0x01;    /* up */
+    public static final int PASSTHROUGH_ID_DOWN        = 0x02;    /* down */
+    public static final int PASSTHROUGH_ID_LEFT        = 0x03;    /* left */
+    public static final int PASSTHROUGH_ID_RIGHT       = 0x04;    /* right */
+    public static final int PASSTHROUGH_ID_RIGHT_UP    = 0x05;    /* right-up */
+    public static final int PASSTHROUGH_ID_RIGHT_DOWN  = 0x06;    /* right-down */
+    public static final int PASSTHROUGH_ID_LEFT_UP     = 0x07;    /* left-up */
+    public static final int PASSTHROUGH_ID_LEFT_DOWN   = 0x08;    /* left-down */
+    public static final int PASSTHROUGH_ID_ROOT_MENU   = 0x09;    /* root menu */
+    public static final int PASSTHROUGH_ID_SETUP_MENU  = 0x0A;    /* setup menu */
+    public static final int PASSTHROUGH_ID_CONT_MENU   = 0x0B;    /* contents menu */
+    public static final int PASSTHROUGH_ID_FAV_MENU    = 0x0C;    /* favorite menu */
+    public static final int PASSTHROUGH_ID_EXIT        = 0x0D;    /* exit */
+    public static final int PASSTHROUGH_ID_0           = 0x20;    /* 0 */
+    public static final int PASSTHROUGH_ID_1           = 0x21;    /* 1 */
+    public static final int PASSTHROUGH_ID_2           = 0x22;    /* 2 */
+    public static final int PASSTHROUGH_ID_3           = 0x23;    /* 3 */
+    public static final int PASSTHROUGH_ID_4           = 0x24;    /* 4 */
+    public static final int PASSTHROUGH_ID_5           = 0x25;    /* 5 */
+    public static final int PASSTHROUGH_ID_6           = 0x26;    /* 6 */
+    public static final int PASSTHROUGH_ID_7           = 0x27;    /* 7 */
+    public static final int PASSTHROUGH_ID_8           = 0x28;    /* 8 */
+    public static final int PASSTHROUGH_ID_9           = 0x29;    /* 9 */
+    public static final int PASSTHROUGH_ID_DOT         = 0x2A;    /* dot */
+    public static final int PASSTHROUGH_ID_ENTER       = 0x2B;    /* enter */
+    public static final int PASSTHROUGH_ID_CLEAR       = 0x2C;    /* clear */
+    public static final int PASSTHROUGH_ID_CHAN_UP     = 0x30;    /* channel up */
+    public static final int PASSTHROUGH_ID_CHAN_DOWN   = 0x31;    /* channel down */
+    public static final int PASSTHROUGH_ID_PREV_CHAN   = 0x32;    /* previous channel */
+    public static final int PASSTHROUGH_ID_SOUND_SEL   = 0x33;    /* sound select */
+    public static final int PASSTHROUGH_ID_INPUT_SEL   = 0x34;    /* input select */
+    public static final int PASSTHROUGH_ID_DISP_INFO   = 0x35;    /* display information */
+    public static final int PASSTHROUGH_ID_HELP        = 0x36;    /* help */
+    public static final int PASSTHROUGH_ID_PAGE_UP     = 0x37;    /* page up */
+    public static final int PASSTHROUGH_ID_PAGE_DOWN   = 0x38;    /* page down */
+    public static final int PASSTHROUGH_ID_POWER       = 0x40;    /* power */
+    public static final int PASSTHROUGH_ID_VOL_UP      = 0x41;    /* volume up */
+    public static final int PASSTHROUGH_ID_VOL_DOWN    = 0x42;    /* volume down */
+    public static final int PASSTHROUGH_ID_MUTE        = 0x43;    /* mute */
+    public static final int PASSTHROUGH_ID_PLAY        = 0x44;    /* play */
+    public static final int PASSTHROUGH_ID_STOP        = 0x45;    /* stop */
+    public static final int PASSTHROUGH_ID_PAUSE       = 0x46;    /* pause */
+    public static final int PASSTHROUGH_ID_RECORD      = 0x47;    /* record */
+    public static final int PASSTHROUGH_ID_REWIND      = 0x48;    /* rewind */
+    public static final int PASSTHROUGH_ID_FAST_FOR    = 0x49;    /* fast forward */
+    public static final int PASSTHROUGH_ID_EJECT       = 0x4A;    /* eject */
+    public static final int PASSTHROUGH_ID_FORWARD     = 0x4B;    /* forward */
+    public static final int PASSTHROUGH_ID_BACKWARD    = 0x4C;    /* backward */
+    public static final int PASSTHROUGH_ID_ANGLE       = 0x50;    /* angle */
+    public static final int PASSTHROUGH_ID_SUBPICT     = 0x51;    /* subpicture */
+    public static final int PASSTHROUGH_ID_F1          = 0x71;    /* F1 */
+    public static final int PASSTHROUGH_ID_F2          = 0x72;    /* F2 */
+    public static final int PASSTHROUGH_ID_F3          = 0x73;    /* F3 */
+    public static final int PASSTHROUGH_ID_F4          = 0x74;    /* F4 */
+    public static final int PASSTHROUGH_ID_F5          = 0x75;    /* F5 */
+    public static final int PASSTHROUGH_ID_VENDOR      = 0x7E;    /* vendor unique */
+    public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80;
+}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
new file mode 100644
index 0000000..b53a8fc
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth AVRCP Controller
+ * profile.
+ *
+ *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothAvrcpController proxy object.
+ *
+ * {@hide}
+ */
+public final class BluetoothAvrcpController implements BluetoothProfile {
+    private static final String TAG = "BluetoothAvrcpController";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent used to broadcast the change in connection state of the AVRCP Controller
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private IBluetoothAvrcpController mService;
+    private BluetoothAdapter mAdapter;
+
+    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 BluetoothAvrcpController proxy object for interacting with the local
+     * Bluetooth AVRCP service.
+     *
+     */
+    /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
+        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(IBluetoothAvrcpController.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 AVRCP Controller Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    /*package*/ void close() {
+        mServiceListener = null;
+        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);
+                }
+            }
+        }
+    }
+
+    public void finalize() {
+        close();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (VDBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (VDBG) log("getState(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
+        if (DBG) Log.d(TAG, "sendPassThroughCmd");
+        if (mService != null && isEnabled()) {
+            try {
+                mService.sendPassThroughCmd(device, keyCode, keyState);
+                return;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e);
+                return;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            mService = IBluetoothAvrcpController.Stub.asInterface(service);
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
+                        BluetoothAvrcpController.this);
+            }
+        }
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private static void log(String msg) {
+      Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
new file mode 100644
index 0000000..ff4ebee
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Public API to control Hands Free Profile (HFP role only).
+ * <p>
+ * This class defines methods that shall be used by application to manage profile
+ * connection, calls states and calls actions.
+ * <p>
+ *
+ * @hide
+ * */
+public final class BluetoothHeadsetClient implements BluetoothProfile {
+    private static final String TAG = "BluetoothHeadsetClient";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent sent whenever connection to remote changes.
+     *
+     * <p>It includes two extras:
+     * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code>
+     * and <code>BluetoothProfile.EXTRA_STATE</code>, which
+     * are mandatory.
+     * <p>There are also non mandatory feature extras:
+     * {@link #EXTRA_AG_FEATURE_3WAY_CALLING},
+     * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION},
+     * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT},
+     * {@link #EXTRA_AG_FEATURE_REJECT_CALL},
+     * {@link #EXTRA_AG_FEATURE_ECC},
+     * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD},
+     * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL},
+     * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL},
+     * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT},
+     * {@link #EXTRA_AG_FEATURE_MERGE},
+     * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH},
+     * sent as boolean values only when <code>EXTRA_STATE</code>
+     * is set to <code>STATE_CONNECTED</code>.</p>
+     *
+     * <p>Note that features supported by AG are being sent as
+     * booleans with value <code>true</code>,
+     * and not supported ones are <strong>not</strong> being sent at all.</p>
+     */
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent sent whenever audio state changes.
+     *
+     * <p>It includes two mandatory extras:
+     * {@link BluetoothProfile.EXTRA_STATE},
+     * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE},
+     * with possible values:
+     * {@link #STATE_AUDIO_CONNECTING},
+     * {@link #STATE_AUDIO_CONNECTED},
+     * {@link #STATE_AUDIO_DISCONNECTED}</p>
+     * <p>When <code>EXTRA_STATE</code> is set
+     * to </code>STATE_AUDIO_CONNECTED</code>,
+     * it also includes {@link #EXTRA_AUDIO_WBS}
+     * indicating wide band speech support.</p>
+     */
+    public static final String ACTION_AUDIO_STATE_CHANGED =
+        "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
+
+    /**
+     * Intent sending updates of the Audio Gateway state.
+     * Each extra is being sent only when value it
+     * represents has been changed recently on AG.
+     * <p>It can contain one or more of the following extras:
+     * {@link #EXTRA_NETWORK_STATUS},
+     * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH},
+     * {@link #EXTRA_NETWORK_ROAMING},
+     * {@link #EXTRA_BATTERY_LEVEL},
+     * {@link #EXTRA_OPERATOR_NAME},
+     * {@link #EXTRA_VOICE_RECOGNITION},
+     * {@link #EXTRA_IN_BAND_RING}</p>
+     */
+    public static final String ACTION_AG_EVENT =
+            "android.bluetooth.headsetclient.profile.action.AG_EVENT";
+
+    /**
+     * Intent sent whenever state of a call changes.
+     *
+     * <p>It includes:
+     * {@link #EXTRA_CALL},
+     * with value of {@link BluetoothHeadsetClientCall} instance,
+     * representing actual call state.</p>
+     */
+    public static final String ACTION_CALL_CHANGED =
+            "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
+
+    /**
+     * Intent that notifies about the result of the last issued action.
+     * Please note that not every action results in explicit action result code being sent.
+     * Instead other notifications about new Audio Gateway state might be sent,
+     * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value
+     * when for example user started voice recognition from HF unit.
+     */
+    public static final String ACTION_RESULT =
+            "android.bluetooth.headsetclient.profile.action.RESULT";
+
+    /**
+     * Intent that notifies about the number attached to the last voice tag
+     * recorded on AG.
+     *
+     * <p>It contains:
+     * {@link #EXTRA_NUMBER},
+     * with a <code>String</code> value representing phone number.</p>
+     */
+    public static final String ACTION_LAST_VTAG =
+            "android.bluetooth.headsetclient.profile.action.LAST_VTAG";
+
+    public static final int STATE_AUDIO_DISCONNECTED = 0;
+    public static final int STATE_AUDIO_CONNECTING = 1;
+    public static final int STATE_AUDIO_CONNECTED = 2;
+
+    /**
+     * Extra with information if connected audio is WBS.
+     * <p>Possible values: <code>true</code>,
+     *                     <code>false</code>.</p>
+     */
+    public static final String EXTRA_AUDIO_WBS =
+            "android.bluetooth.headsetclient.extra.AUDIO_WBS";
+
+    /**
+     * Extra for AG_EVENT indicates network status.
+     * <p>Value: 0 - network unavailable,
+     *           1 - network available </p>
+     */
+    public static final String EXTRA_NETWORK_STATUS =
+            "android.bluetooth.headsetclient.extra.NETWORK_STATUS";
+    /**
+     * Extra for AG_EVENT intent indicates network signal strength.
+     * <p>Value: <code>Integer</code> representing signal strength.</p>
+     */
+    public static final String EXTRA_NETWORK_SIGNAL_STRENGTH =
+            "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH";
+    /**
+     * Extra for AG_EVENT intent indicates roaming state.
+     * <p>Value: 0 - no roaming
+     *           1 - active roaming</p>
+     */
+    public static final String EXTRA_NETWORK_ROAMING =
+            "android.bluetooth.headsetclient.extra.NETWORK_ROAMING";
+    /**
+     * Extra for AG_EVENT intent indicates the battery level.
+     * <p>Value: <code>Integer</code> representing signal strength.</p>
+     */
+    public static final String EXTRA_BATTERY_LEVEL =
+            "android.bluetooth.headsetclient.extra.BATTERY_LEVEL";
+    /**
+     * Extra for AG_EVENT intent indicates operator name.
+     * <p>Value: <code>String</code> representing operator name.</p>
+     */
+    public static final String EXTRA_OPERATOR_NAME =
+            "android.bluetooth.headsetclient.extra.OPERATOR_NAME";
+    /**
+     * Extra for AG_EVENT intent indicates voice recognition state.
+     * <p>Value:
+     *          0 - voice recognition stopped,
+     *          1 - voice recognition started.</p>
+     */
+    public static final String EXTRA_VOICE_RECOGNITION =
+            "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION";
+    /**
+     * Extra for AG_EVENT intent indicates in band ring state.
+     * <p>Value:
+     *          0 - in band ring tone not supported, or
+     *          1 - in band ring tone supported.</p>
+     */
+    public static final String EXTRA_IN_BAND_RING =
+            "android.bluetooth.headsetclient.extra.IN_BAND_RING";
+
+    /**
+     * Extra for AG_EVENT intent indicates subscriber info.
+     * <p>Value: <code>String</code> containing subscriber information.</p>
+     */
+    public static final String EXTRA_SUBSCRIBER_INFO =
+            "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO";
+
+    /**
+     *  Extra for AG_CALL_CHANGED intent indicates the
+     *  {@link BluetoothHeadsetClientCall} object that has changed.
+     */
+    public static final String EXTRA_CALL =
+            "android.bluetooth.headsetclient.extra.CALL";
+
+    /**
+     * Extra for ACTION_LAST_VTAG intent.
+     * <p>Value: <code>String</code> representing phone number
+     * corresponding to last voice tag recorded on AG</p>
+     */
+    public static final String EXTRA_NUMBER =
+            "android.bluetooth.headsetclient.extra.NUMBER";
+
+    /**
+     * Extra for ACTION_RESULT intent that shows the result code of
+     * last issued action.
+     * <p>Possible results:
+     * {@link #ACTION_RESULT_OK},
+     * {@link #ACTION_RESULT_ERROR},
+     * {@link #ACTION_RESULT_ERROR_NO_CARRIER},
+     * {@link #ACTION_RESULT_ERROR_BUSY},
+     * {@link #ACTION_RESULT_ERROR_NO_ANSWER},
+     * {@link #ACTION_RESULT_ERROR_DELAYED},
+     * {@link #ACTION_RESULT_ERROR_BLACKLISTED},
+     * {@link #ACTION_RESULT_ERROR_CME}</p>
+     */
+    public static final String EXTRA_RESULT_CODE =
+            "android.bluetooth.headsetclient.extra.RESULT_CODE";
+
+    /**
+     * Extra for ACTION_RESULT intent that shows the extended result code of
+     * last issued action.
+     * <p>Value: <code>Integer</code> - error code.</p>
+     */
+    public static final String EXTRA_CME_CODE =
+            "android.bluetooth.headsetclient.extra.CME_CODE";
+
+    /* Extras for AG_FEATURES, extras type is boolean */
+    // TODO verify if all of those are actually useful
+    /**
+     * AG feature: three way calling.
+     */
+    public final static String EXTRA_AG_FEATURE_3WAY_CALLING =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING";
+    /**
+     * AG feature: voice recognition.
+     */
+    public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION";
+    /**
+     * AG feature: fetching phone number for voice tagging procedure.
+     */
+    public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT";
+    /**
+     * AG feature: ability to reject incoming call.
+     */
+    public final static String EXTRA_AG_FEATURE_REJECT_CALL =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL";
+    /**
+     * AG feature: enhanced call handling (terminate specific call, private consultation).
+     */
+    public final static String EXTRA_AG_FEATURE_ECC =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC";
+    /**
+     * AG feature: response and hold.
+     */
+    public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD";
+    /**
+     * AG call handling feature: accept held or waiting call in three way calling scenarios.
+     */
+    public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL";
+    /**
+     * AG call handling feature: release held or waiting call in three way calling scenarios.
+     */
+    public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL";
+    /**
+     * AG call handling feature: release active call and accept held or waiting call in three way
+     * calling scenarios.
+     */
+    public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT";
+    /**
+     * AG call handling feature: merge two calls, held and active - multi party conference mode.
+     */
+    public final static String EXTRA_AG_FEATURE_MERGE =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE";
+    /**
+     * AG call handling feature: merge calls and disconnect from multi party
+     * conversation leaving peers connected to each other.
+     * Note that this feature needs to be supported by mobile network operator
+     * as it requires connection and billing transfer.
+     */
+    public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH =
+            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH";
+
+    /* Action result codes */
+    public final static int ACTION_RESULT_OK = 0;
+    public final static int ACTION_RESULT_ERROR = 1;
+    public final static int ACTION_RESULT_ERROR_NO_CARRIER = 2;
+    public final static int ACTION_RESULT_ERROR_BUSY = 3;
+    public final static int ACTION_RESULT_ERROR_NO_ANSWER = 4;
+    public final static int ACTION_RESULT_ERROR_DELAYED = 5;
+    public final static int ACTION_RESULT_ERROR_BLACKLISTED = 6;
+    public final static int ACTION_RESULT_ERROR_CME = 7;
+
+    /* Detailed CME error codes */
+    public final static int CME_PHONE_FAILURE                           = 0;
+    public final static int CME_NO_CONNECTION_TO_PHONE                  = 1;
+    public final static int CME_OPERATION_NOT_ALLOWED                   = 3;
+    public final static int CME_OPERATION_NOT_SUPPORTED                 = 4;
+    public final static int CME_PHSIM_PIN_REQUIRED                      = 5;
+    public final static int CME_PHFSIM_PIN_REQUIRED                     = 6;
+    public final static int CME_PHFSIM_PUK_REQUIRED                     = 7;
+    public final static int CME_SIM_NOT_INSERTED                        = 10;
+    public final static int CME_SIM_PIN_REQUIRED                        = 11;
+    public final static int CME_SIM_PUK_REQUIRED                        = 12;
+    public final static int CME_SIM_FAILURE                             = 13;
+    public final static int CME_SIM_BUSY                                = 14;
+    public final static int CME_SIM_WRONG                               = 15;
+    public final static int CME_INCORRECT_PASSWORD                      = 16;
+    public final static int CME_SIM_PIN2_REQUIRED                       = 17;
+    public final static int CME_SIM_PUK2_REQUIRED                       = 18;
+    public final static int CME_MEMORY_FULL                             = 20;
+    public final static int CME_INVALID_INDEX                           = 21;
+    public final static int CME_NOT_FOUND                               = 22;
+    public final static int CME_MEMORY_FAILURE                          = 23;
+    public final static int CME_TEXT_STRING_TOO_LONG                    = 24;
+    public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING        = 25;
+    public final static int CME_DIAL_STRING_TOO_LONG                    = 26;
+    public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING        = 27;
+    public final static int CME_NO_NETWORK_SERVICE                      = 30;
+    public final static int CME_NETWORK_TIMEOUT                         = 31;
+    public final static int CME_EMERGENCY_SERVICE_ONLY                  = 32;
+    public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS            = 33;
+    public final static int CME_NOT_SUPPORTED_FOR_VOIP                  = 34;
+    public final static int CME_SIP_RESPONSE_CODE                       = 35;
+    public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED    = 40;
+    public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED    = 41;
+    public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED   = 42;
+    public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED   = 43;
+    public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44;
+    public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45;
+    public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED  = 46;
+    public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED  = 47;
+    public final static int CME_HIDDEN_KEY_REQUIRED                     = 48;
+    public final static int CME_EAP_NOT_SUPPORTED                       = 49;
+    public final static int CME_INCORRECT_PARAMETERS                    = 50;
+
+    /* Action policy for other calls when accepting call */
+    public static final int CALL_ACCEPT_NONE = 0;
+    public static final int CALL_ACCEPT_HOLD = 1;
+    public static final int CALL_ACCEPT_TERMINATE = 2;
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private IBluetoothHeadsetClient mService;
+    private BluetoothAdapter mAdapter;
+
+    final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                @Override
+                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...");
+                                    Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
+                                    doBind();
+                                }
+                            } catch (Exception re) {
+                                Log.e(TAG,"",re);
+                            }
+                        }
+                    }
+                }
+        };
+
+    /**
+     * Create a BluetoothHeadsetClient proxy object.
+     */
+    /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) {
+        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(IBluetoothHeadsetClient.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 Headset Client Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Close the connection to the backing service.
+     * Other public functions of BluetoothHeadsetClient will return default error
+     * results once close() has been called. Multiple invocations of close()
+     * are ok.
+     */
+    /*package*/ void close() {
+        if (VDBG) log("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;
+    }
+
+    /**
+     * Connects to remote device.
+     *
+     * Currently, the system supports only 1 connection. So, in case of the
+     * second connection, this implementation will disconnect already connected
+     * device automatically and will process the new one.
+     *
+     * @param device    a remote device we want connect to
+     * @return <code>true</code> if command has been issued successfully;
+     *          <code>false</code> otherwise;
+     *          upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *          intent.
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.connect(device);
+            } 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;
+    }
+
+    /**
+     * Disconnects remote device
+     *
+     * @param device    a remote device we want disconnect
+     * @return          <code>true</code> if command has been issued successfully;
+     *                  <code>false</code> otherwise;
+     *                  upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *                  intent.
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } 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;
+    }
+
+    /**
+     * Return the list of connected remote devices
+     *
+     * @return list of connected devices; empty list if nothing is connected.
+     */
+    @Override
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Returns list of remote devices in a particular state
+     *
+     * @param states    collection of states
+     * @return          list of devices that state matches the states listed in
+     *                  <code>states</code>; empty list if nothing matches the
+     *                  <code>states</code>
+     */
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (VDBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Returns state of the <code>device</code>
+     *
+     * @param device    a remote device
+     * @return          the state of connection of the device
+     */
+    @Override
+    public int getConnectionState(BluetoothDevice device) {
+        if (VDBG) log("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
+     *
+     * The device should already be paired.
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("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.
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("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;
+    }
+
+    /**
+     * Starts voice recognition.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_AG_EVENT}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean startVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("startVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.startVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Stops voice recognition.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_AG_EVENT}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean stopVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("stopVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.stopVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Returns list of all calls in any state.
+     *
+     * @param device    remote device
+     * @return          list of calls; empty list if none call exists
+     */
+    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
+        if (DBG) log("getCurrentCalls()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.getCurrentCalls(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return null;
+    }
+
+    /**
+     * Returns list of current values of AG indicators.
+     *
+     * @param device    remote device
+     * @return          bundle of AG  indicators; null if device is not in
+     *                  CONNECTED state
+     */
+    public Bundle getCurrentAgEvents(BluetoothDevice device) {
+        if (DBG) log("getCurrentCalls()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.getCurrentAgEvents(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return null;
+    }
+
+    /**
+     * Accepts a call
+     *
+     * @param device    remote device
+     * @param flag      action policy while accepting a call. Possible values
+     *                   {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD},
+     *                   {@link #CALL_ACCEPT_TERMINATE}
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     */
+    public boolean acceptCall(BluetoothDevice device, int flag) {
+        if (DBG) log("acceptCall()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.acceptCall(device, flag);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Holds a call.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     */
+    public boolean holdCall(BluetoothDevice device) {
+        if (DBG) log("holdCall()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.holdCall(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Rejects a call.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_REJECT_CALL}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean rejectCall(BluetoothDevice device) {
+        if (DBG) log("rejectCall()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.rejectCall(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Terminates a specified call.
+     *
+     * Works only when Extended Call Control is supported by Audio Gateway.
+     *
+     * @param device    remote device
+     * @param index     index of the call to be terminated
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_ECC}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean terminateCall(BluetoothDevice device, int index) {
+        if (DBG) log("terminateCall()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.terminateCall(device, index);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Enters private mode with a specified call.
+     *
+     * Works only when Extended Call Control is supported by Audio Gateway.
+     *
+     * @param device    remote device
+     * @param index     index of the call to connect in private mode
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_ECC}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean enterPrivateMode(BluetoothDevice device, int index) {
+        if (DBG) log("enterPrivateMode()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.enterPrivateMode(device, index);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Performs explicit call transfer.
+     *
+     * That means connect other calls and disconnect.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent.
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean explicitCallTransfer(BluetoothDevice device) {
+        if (DBG) log("explicitCallTransfer()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.explicitCallTransfer(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Redials last number from Audio Gateway.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent in case of success; {@link #ACTION_RESULT} is sent
+     *                   otherwise;
+     */
+    public boolean redial(BluetoothDevice device) {
+        if (DBG) log("redial()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.redial(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Places a call with specified number.
+     *
+     * @param device    remote device
+     * @param number    valid phone number
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent in case of success; {@link #ACTION_RESULT} is sent
+     *                   otherwise;
+     */
+    public boolean dial(BluetoothDevice device, String number) {
+        if (DBG) log("dial()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.dial(device, number);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Places a call to the number under specified memory location.
+     *
+     * @param device    remote device
+     * @param location  valid memory location
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_CALL_CHANGED}
+     *                   intent in case of success; {@link #ACTION_RESULT} is sent
+     *                   otherwise;
+     */
+    public boolean dialMemory(BluetoothDevice device, int location) {
+        if (DBG) log("dialMemory()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.dialMemory(device, location);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Sends DTMF code.
+     *
+     * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,#
+     *
+     * @param device    remote device
+     * @param code  ASCII code
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_RESULT} intent;
+     */
+    public boolean sendDTMF(BluetoothDevice device, byte code) {
+        if (DBG) log("sendDTMF()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.sendDTMF(device, code);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get a number corresponding to last voice tag recorded on AG.
+     *
+     * @param device    remote device
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_LAST_VTAG}
+     *                   or {@link #ACTION_RESULT} intent;
+     *
+     * <p>Feature required for successful execution is being reported by:
+     * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}.
+     * This method invocation will fail silently when feature is not supported.</p>
+     */
+    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
+        if (DBG) log("getLastVoiceTagNumber()");
+        if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
+            try {
+                return mService.getLastVoiceTagNumber(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Accept the incoming connection.
+     */
+    public boolean acceptIncomingConnect(BluetoothDevice device) {
+        if (DBG) log("acceptIncomingConnect");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.acceptIncomingConnect(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;
+    }
+
+    /**
+     * Reject the incoming connection.
+     */
+    public boolean rejectIncomingConnect(BluetoothDevice device) {
+        if (DBG) log("rejectIncomingConnect");
+        if (mService != null) {
+            try {
+                return mService.rejectIncomingConnect(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;
+    }
+
+    /**
+     * Returns current audio state of Audio Gateway.
+     *
+     * Note: This is an internal function and shouldn't be exposed
+     */
+    public int getAudioState(BluetoothDevice device) {
+        if (VDBG) log("getAudioState");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getAudioState(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 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
+    }
+
+    /**
+     * Initiates a connection of audio channel.
+     *
+     * It setup SCO channel with remote connected Handsfree AG device.
+     *
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
+     *                   intent;
+     */
+    public boolean connectAudio() {
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.connectAudio();
+            } 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;
+    }
+
+    /**
+     * Disconnects audio channel.
+     *
+     * It tears down the SCO channel from remote AG device.
+     *
+     * @return          <code>true</code> if command has been issued successfully;
+     *                   <code>false</code> otherwise;
+     *                   upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
+     *                   intent;
+     */
+    public boolean disconnectAudio() {
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.disconnectAudio();
+            } 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;
+    }
+
+    /**
+     * Get Audio Gateway features
+     *
+     * @param device    remote device
+     * @return          bundle of AG features; null if no service or
+     *                  AG not connected
+     */
+    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getCurrentAgFeatures(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 null;
+    }
+
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            mService = IBluetoothHeadsetClient.Stub.asInterface(service);
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,
+                        BluetoothHeadsetClient.this);
+            }
+        }
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.aidl b/core/java/android/bluetooth/BluetoothHeadsetClientCall.aidl
new file mode 100644
index 0000000..35f7923
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+parcelable BluetoothHeadsetClientCall;
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
new file mode 100644
index 0000000..a15bd97
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents a single call, its state and properties.
+ * It implements {@link Parcelable} for inter-process message passing.
+ * @hide
+ */
+public final class BluetoothHeadsetClientCall implements Parcelable {
+
+    /* Call state */
+    /**
+     * Call is active.
+     */
+    public static final int CALL_STATE_ACTIVE = 0;
+    /**
+     * Call is in held state.
+     */
+    public static final int CALL_STATE_HELD = 1;
+    /**
+     * Outgoing call that is being dialed right now.
+     */
+    public static final int CALL_STATE_DIALING = 2;
+    /**
+     * Outgoing call that remote party has already been alerted about.
+     */
+    public static final int CALL_STATE_ALERTING = 3;
+    /**
+     * Incoming call that can be accepted or rejected.
+     */
+    public static final int CALL_STATE_INCOMING = 4;
+    /**
+     * Waiting call state when there is already an active call.
+     */
+    public static final int CALL_STATE_WAITING = 5;
+    /**
+     * Call that has been held by response and hold
+     * (see Bluetooth specification for further references).
+     */
+    public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6;
+    /**
+     * Call that has been already terminated and should not be referenced as a valid call.
+     */
+    public static final int CALL_STATE_TERMINATED = 7;
+
+    private final int mId;
+    private int mState;
+    private String mNumber;
+    private boolean mMultiParty;
+    private final boolean mOutgoing;
+
+    /**
+     * Creates BluetoothHeadsetClientCall instance.
+     */
+    public BluetoothHeadsetClientCall(int id, int state, String number, boolean multiParty,
+            boolean outgoing) {
+        mId = id;
+        mState = state;
+        mNumber = number != null ? number : "";
+        mMultiParty = multiParty;
+        mOutgoing = outgoing;
+    }
+
+    /**
+     * Sets call's state.
+     *
+     * <p>Note: This is an internal function and shouldn't be exposed</p>
+     *
+     * @param  state    new call state.
+     */
+    public void setState(int state) {
+        mState = state;
+    }
+
+    /**
+     * Sets call's number.
+     *
+     * <p>Note: This is an internal function and shouldn't be exposed</p>
+     *
+     * @param number    String representing phone number.
+     */
+    public void setNumber(String number) {
+        mNumber = number;
+    }
+
+    /**
+     * Sets this call as multi party call.
+     *
+     * <p>Note: This is an internal function and shouldn't be exposed</p>
+     *
+     * @param multiParty    if <code>true</code> sets this call as a part
+     *                      of multi party conference.
+     */
+    public void setMultiParty(boolean multiParty) {
+        mMultiParty = multiParty;
+    }
+
+    /**
+     * Gets call's Id.
+     *
+     * @return call id.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Gets call's current state.
+     *
+     * @return state of this particular phone call.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Gets call's number.
+     *
+     * @return string representing phone number.
+     */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Checks if call is an active call in a conference mode (aka multi party).
+     *
+     * @return <code>true</code> if call is a multi party call,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isMultiParty() {
+        return mMultiParty;
+    }
+
+    /**
+     * Checks if this call is an outgoing call.
+     *
+     * @return <code>true</code> if its outgoing call,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isOutgoing() {
+        return mOutgoing;
+    }
+
+    public String toString() {
+        StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mId: ");
+        builder.append(mId);
+        builder.append(", mState: ");
+        switch (mState) {
+            case CALL_STATE_ACTIVE: builder.append("ACTIVE"); break;
+            case CALL_STATE_HELD: builder.append("HELD"); break;
+            case CALL_STATE_DIALING: builder.append("DIALING"); break;
+            case CALL_STATE_ALERTING: builder.append("ALERTING"); break;
+            case CALL_STATE_INCOMING: builder.append("INCOMING"); break;
+            case CALL_STATE_WAITING: builder.append("WAITING"); break;
+            case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: builder.append("HELD_BY_RESPONSE_AND_HOLD"); break;
+            case CALL_STATE_TERMINATED: builder.append("TERMINATED"); break;
+            default: builder.append(mState); break;
+        }
+        builder.append(", mNumber: ");
+        builder.append(mNumber);
+        builder.append(", mMultiParty: ");
+        builder.append(mMultiParty);
+        builder.append(", mOutgoing: ");
+        builder.append(mOutgoing);
+        builder.append("}");
+        return builder.toString();
+    }
+
+    /**
+     * {@link Parcelable.Creator} interface implementation.
+     */
+    public static final Parcelable.Creator<BluetoothHeadsetClientCall> CREATOR =
+            new Parcelable.Creator<BluetoothHeadsetClientCall>() {
+                @Override
+                public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
+                    return new BluetoothHeadsetClientCall(in.readInt(), in.readInt(),
+                            in.readString(), in.readInt() == 1, in.readInt() == 1);
+                }
+
+                @Override
+                public BluetoothHeadsetClientCall[] newArray(int size) {
+                    return new BluetoothHeadsetClientCall[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mId);
+        out.writeInt(mState);
+        out.writeString(mNumber);
+        out.writeInt(mMultiParty ? 1 : 0);
+        out.writeInt(mOutgoing ? 1 : 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index d898060..1367405 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2010-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,6 +110,18 @@
     public static final int A2DP_SINK = 10;
 
     /**
+     * AVRCP Controller Profile
+     * @hide
+     */
+    public static final int AVRCP_CONTROLLER = 11;
+
+    /**
+     * Headset Client - HFP HF Role
+     * @hide
+     */
+    public static final int HEADSET_CLIENT = 16;
+
+    /**
      * 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/IBluetoothA2dpSink.aidl b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
new file mode 100644
index 0000000..b7c6476
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothAudioConfig;
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * APIs for Bluetooth A2DP sink service
+ *
+ * @hide
+ */
+interface IBluetoothA2dpSink {
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    BluetoothAudioConfig getAudioConfig(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
new file mode 100644
index 0000000..f917a50
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * APIs for Bluetooth AVRCP controller service
+ *
+ * @hide
+ */
+interface IBluetoothAvrcpController {
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
+}
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
new file mode 100644
index 0000000..e518b7d
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClientCall;
+import android.os.Bundle;
+
+/**
+ * API for Bluetooth Headset Client service (HFP HF Role)
+ *
+ * {@hide}
+ */
+interface IBluetoothHeadsetClient {
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+
+    boolean acceptIncomingConnect(in BluetoothDevice device);
+    boolean rejectIncomingConnect(in BluetoothDevice device);
+
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+
+    boolean startVoiceRecognition(in BluetoothDevice device);
+    boolean stopVoiceRecognition(in BluetoothDevice device);
+
+    List<BluetoothHeadsetClientCall> getCurrentCalls(in BluetoothDevice device);
+    Bundle getCurrentAgEvents(in BluetoothDevice device);
+
+    boolean acceptCall(in BluetoothDevice device, int flag);
+    boolean holdCall(in BluetoothDevice device);
+    boolean rejectCall(in BluetoothDevice device);
+    boolean terminateCall(in BluetoothDevice device, int index);
+
+    boolean enterPrivateMode(in BluetoothDevice device, int index);
+    boolean explicitCallTransfer(in BluetoothDevice device);
+
+    boolean redial(in BluetoothDevice device);
+    boolean dial(in BluetoothDevice device, String number);
+    boolean dialMemory(in BluetoothDevice device, int location);
+
+    boolean sendDTMF(in BluetoothDevice device, byte code);
+    boolean getLastVoiceTagNumber(in BluetoothDevice device);
+
+    int getAudioState(in BluetoothDevice device);
+    boolean connectAudio();
+    boolean disconnectAudio();
+
+    Bundle getCurrentAgFeatures(in BluetoothDevice device);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 453ee17..f75ae55 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13529,6 +13529,10 @@
             throw new IllegalStateException("This view must be attached to a window first");
         }
 
+        if (getWidth() == 0 || getHeight() == 0) {
+            return;
+        }
+
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
                 // The only part of a hardware layer we can build in response to
diff --git a/core/java/android/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java
index 588b868..4a7f5fd 100644
--- a/core/java/android/webkit/ClientCertRequest.java
+++ b/core/java/android/webkit/ClientCertRequest.java
@@ -27,7 +27,7 @@
  * such as the host name and the port number requesting the cert, the acceptable
  * key types and the principals.
  *
- * The user should call one of the interface methods to indicate how to deal
+ * The user should call one of the class methods to indicate how to deal
  * with the client certificate request. All methods should be called on
  * UI thread.
  *
@@ -37,42 +37,45 @@
  * {@link WebView#clearClientCertPreferences}.
  *
  */
-public interface ClientCertRequest {
+public abstract class ClientCertRequest {
+
+    public ClientCertRequest() { }
+
     /**
      * Returns the acceptable types of asymmetric keys (can be null).
      */
-    public String[] getKeyTypes();
+    public abstract String[] getKeyTypes();
 
     /**
      * Returns the acceptable certificate issuers for the certificate
      *            matching the private key (can be null).
      */
-    public Principal[] getPrincipals();
+    public abstract Principal[] getPrincipals();
 
     /**
      * Returns the host name of the server requesting the certificate.
      */
-    public String getHost();
+    public abstract String getHost();
 
     /**
      * Returns the port number of the server requesting the certificate.
      */
-    public int getPort();
+    public abstract int getPort();
 
     /**
      * Proceed with the specified private key and client certificate chain.
      * Remember the user's positive choice and use it for future requests.
      */
-    public void proceed(PrivateKey privateKey, X509Certificate[] chain);
+    public abstract void proceed(PrivateKey privateKey, X509Certificate[] chain);
 
     /**
      * Ignore the request for now. Do not remember user's choice.
      */
-    public void ignore();
+    public abstract void ignore();
 
     /**
      * Cancel this request. Remember the user's choice and use it for
      * future requests.
      */
-    public void cancel();
+    public abstract void cancel();
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 91ca7b4..482c7e8 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1486,11 +1486,11 @@
     }
 
     /**
-     * Clears the client certificate preferences table stored in response
-     * to proceeding/cancelling client cert requests. Note that webview
+     * Clears the client certificate preferences stored in response
+     * to proceeding/cancelling client cert requests. Note that Webview
      * automatically clears these preferences when it receives a
-     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The client certificate
-     * preferences are global for all Webviews.
+     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
+     * shared by all the webviews that are created by the embedder application.
      *
      * @param onCleared  A runnable to be invoked when client certs are cleared.
      *                   The embedder can pass null if not interested in the
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3067cdd0..b628b7c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -123,10 +123,30 @@
     <protected-broadcast
         android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
     <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
+   <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
diff --git a/core/res/assets/images/android-logo-mask.png b/core/res/assets/images/android-logo-mask.png
index 3078b28..ad40645 100644
--- a/core/res/assets/images/android-logo-mask.png
+++ b/core/res/assets/images/android-logo-mask.png
Binary files differ
diff --git a/core/res/assets/images/android-logo-shine.png b/core/res/assets/images/android-logo-shine.png
index add6796..cb65f22 100644
--- a/core/res/assets/images/android-logo-shine.png
+++ b/core/res/assets/images/android-logo-shine.png
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index a5a074c..e1dec9d 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -537,4 +537,3 @@
         }
     }
 }
-
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 16548d0..1ee0224 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -91,10 +91,10 @@
 
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
-        boolean changed = super.setVisible(visible, restart);
+        final boolean changed = super.setVisible(visible, restart);
         if (visible) {
             if (changed || restart) {
-                setFrame(0, true, restart || mCurFrame >= 0);
+                setFrame(0, true, mAnimating);
             }
         } else {
             unscheduleSelf(this);
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 0d70e75..345400e 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -38,15 +38,18 @@
  */
 class Ripple {
     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-    private static final TimeInterpolator DECEL_INTERPOLATOR = new DecelerateInterpolator(4);
+    private static final TimeInterpolator DECEL_INTERPOLATOR = new LogInterpolator();
 
     private static final float GLOBAL_SPEED = 1.0f;
     private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
-    private static final float WAVE_TOUCH_UP_ACCELERATION = 3096.0f * GLOBAL_SPEED;
-    private static final float WAVE_OPACITY_DECAY_VELOCITY = 1.9f / GLOBAL_SPEED;
-    private static final float WAVE_OUTER_OPACITY_VELOCITY = 1.2f * GLOBAL_SPEED;
+    private static final float WAVE_TOUCH_UP_ACCELERATION = 3400.0f * GLOBAL_SPEED;
+    private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED;
+    private static final float WAVE_OUTER_OPACITY_VELOCITY_MAX = 4.5f * GLOBAL_SPEED;
+    private static final float WAVE_OUTER_OPACITY_VELOCITY_MIN = 1.5f * GLOBAL_SPEED;
+    private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
+    private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
 
-    private static final long RIPPLE_ENTER_DELAY = 100;
+    private static final long RIPPLE_ENTER_DELAY = 80;
 
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<>();
@@ -311,7 +314,7 @@
     public void enter() {
         final int radiusDuration = (int)
                 (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
-        final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY);
+        final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY_MIN);
 
         final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
         radius.setAutoCancel(true);
@@ -355,7 +358,6 @@
      */
     public void exit() {
         cancelSoftwareAnimations();
-
         final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final float remaining;
         if (mAnimRadius != null && mAnimRadius.isRunning()) {
@@ -368,13 +370,23 @@
                 + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
         final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
 
+        // Scale the outer max opacity and opacity velocity based
+        // on the size of the outer radius
+
+        float outerSizeInfluence = MathUtils.constrain(
+                (mOuterRadius - WAVE_OUTER_SIZE_INFLUENCE_MIN * mDensity)
+                / (WAVE_OUTER_SIZE_INFLUENCE_MAX * mDensity), 0, 1);
+        float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_VELOCITY_MIN,
+                WAVE_OUTER_OPACITY_VELOCITY_MAX, outerSizeInfluence);
+
         // Determine at what time the inner and outer opacity intersect.
         // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
         // outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
+
         final int outerInflection = Math.max(0, (int) (1000 * (mOpacity - mOuterOpacity)
-                / (WAVE_OPACITY_DECAY_VELOCITY + WAVE_OUTER_OPACITY_VELOCITY) + 0.5f));
+                / (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
         final int inflectionOpacity = (int) (255 * (mOuterOpacity + outerInflection
-                * WAVE_OUTER_OPACITY_VELOCITY / 1000) + 0.5f);
+                * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
 
         if (mCanUseHardware) {
             exitHardware(radiusDuration, opacityDuration, outerInflection, inflectionOpacity);
@@ -606,4 +618,14 @@
             removeSelf();
         }
     };
+
+    /**
+    * Interpolator with a smooth log deceleration
+    */
+    private static final class LogInterpolator implements TimeInterpolator {
+        @Override
+        public float getInterpolation(float input) {
+            return 1 - (float) Math.pow(400, -input * 1.4);
+        }
+    }
 }
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index fa5a0f0..b28624f 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
index d88397e..9ce434d 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
index 59d4695..51dce69 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
index bbebcb1..3f3e288 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
index 93dc268..9856cbf 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
index 0073872..6910456 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
index 0073872..e8c6ec6 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
index 8a3d69c..06dcd20 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
index 0afd6fe..240f536 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
index 6ca3652..e464347 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
index 66f5c06..f8b59e8 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
index e49a2af..1b578a6 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
index 84879b9..80ecaba 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
index d005f61..c7bf2c7 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
index dca30ab..278de42 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
index 6915a4a..464831c 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
index 309fd4e..c24132c 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
index 23b570e..bc84f22 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
index 1c5f73f..f4e219e 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
index 40cf808..ece2e37 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
index 36dd2be..d524dcf 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
index 3b979cd..373e84a 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
index e8d1dea..e5a102a 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
index 0ab0991..6b19593 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
index 4ced46b..a5e0686 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
index 891397c5..f878093 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
index 5cf3855..7780b16 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
index edb5b1f..343d0dd 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
index 3212336..8e9583b 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
index 7aff620..7d38703 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
index 9f66c14..6e186a3 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
index 9f66c14..12b4042 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
index 972b1a8..e2a89c3 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
index 473b0cb..311720b 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
index 8ad60a5..67e4ee7 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
index 17cc722..dfef430 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
index 6fa0d5f..de6d109 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
index 70b3ef6..7d0c3fbe 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
index be6a5ef..3baeb52 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
index b26600d..d5b48e8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
index b26600d..26d7e7b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
index f2cb7be..c49198d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
index 170bdb6..2a21f29 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
index b4bea6d..538d7d1 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
index eb0eca1..eb8e80b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
index 3d8d19b..1c2c35a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
index 0f6fdad..eec613f 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
index cbe5f77..1c0bad8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
index 80eb803..1293fa8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
index 80eb803..96a9cce 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
index a6d892f..9ffc80a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
index 7183b81..aa9b57b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
index 657d546..b343522 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
index df23594..f2f7b50 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
index ab27aec..383398c 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
index 31f98e1..53444ab 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
index 104aed7..4294937 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
index 18e113a..7cb3ee4 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
index 18e113a..b017946 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
index 079e68d..e8dd07c 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
index c38b43a..f396ea9 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
index c9507af..1b817cf 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
index 2426776..21b7a06 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
index 48fb781..676fc14 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
index c6fb9bf..26f4ba9 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
index 528df6d..ea21b60 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
index 055070a..c7d77c2 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
index 055070a..ae12cfd 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
index e17e8f4..8c27d45 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
index aafee03..c98827c 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
index e3cf7a6..ec2951d 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
index 7e034b8..5103190 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
index a4b3a684..818aa4f 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
index 321fadb..254f757 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
index 6e3219f..f0de417 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
index 2b6021b..97f1526 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
index 2b6021b..773fa94 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
index 023ca54..8a8e941 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
index 3c9dccb..ad7dfc3 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
old mode 100755
new mode 100644
index d8f1703..77969b8
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
old mode 100755
new mode 100644
index 4e12cc9..b171f92
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
old mode 100755
new mode 100644
index 37951a0..ad35c65
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
old mode 100755
new mode 100644
index 7281482..d60229f
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
old mode 100755
new mode 100644
index a13418c..8fffa8e
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
old mode 100755
new mode 100644
index 066c2d9..9215055
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
old mode 100755
new mode 100644
index a619a94..bcf7eb1
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
old mode 100755
new mode 100644
index 115109a..a261f85
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
old mode 100755
new mode 100644
index 3101cb0..4c612f7
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 09a94f5..c8851dc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -120,11 +120,13 @@
      duration of the transition in to recents. -->
     <integer name="recents_animate_task_bar_enter_delay">225</integer>
     <!-- The min animation duration for animating the task bar out. -->
+    <integer name="recents_animate_task_exit_to_home_duration">225</integer>
+    <!-- The min animation duration for animating the task bar out. -->
     <integer name="recents_animate_task_bar_exit_duration">125</integer>
     <!-- The min animation duration for animating the task in when transitioning from home. -->
-    <integer name="recents_animate_task_enter_from_home_duration">325</integer>
+    <integer name="recents_animate_task_enter_from_home_duration">275</integer>
     <!-- The animation stagger to apply to each task animation when transitioning from home. -->
-    <integer name="recents_animate_task_enter_from_home_delay">16</integer>
+    <integer name="recents_animate_task_enter_from_home_delay">10</integer>
     <!-- The min animation duration for animating the nav bar scrim in. -->
     <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
     <!-- The animation duration for animating the removal of a task view. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 8861752..3e61f87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -287,7 +287,7 @@
 
     /** Updates each of the task animation rects. */
     void updateAnimationRects() {
-        if (mServiceIsBound && mBootCompleted) {
+        if (mServiceIsBound) {
             Resources res = mContext.getResources();
             int statusBarHeight = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.status_bar_height);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index cd4d206..1d355cd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -26,7 +26,7 @@
 
         public static class App {
             // Enables the home->Recents transition
-            public static final boolean EnableHomeTransition = false;
+            public static final boolean EnableHomeTransition = true;
             // Enables the screenshot app->Recents transition
             public static final boolean EnableScreenshotAppTransition = false;
             // Enables the filtering of tasks according to their grouping
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index b1f3733..433dcb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -70,6 +70,7 @@
     /** Task view animation and styles */
     public int taskViewEnterFromHomeDuration;
     public int taskViewEnterFromHomeDelay;
+    public int taskViewExitToHomeDuration;
     public int taskViewRemoveAnimDuration;
     public int taskViewRemoveAnimTranslationXPx;
     public int taskViewTranslationZMinPx;
@@ -174,6 +175,8 @@
                 res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
         taskViewEnterFromHomeDelay =
                 res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
+        taskViewExitToHomeDuration =
+                res.getInteger(R.integer.recents_animate_task_exit_to_home_duration);
         taskViewRemoveAnimDuration =
                 res.getInteger(R.integer.recents_animate_task_view_remove_duration);
         taskViewRemoveAnimTranslationXPx =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 7376255..ffd4135 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -327,8 +327,8 @@
         animate()
                 .translationY(ctx.offscreenTranslationY)
                 .setStartDelay(0)
-                .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                .setDuration(mConfig.taskViewEnterFromHomeDuration)
+                .setInterpolator(mConfig.fastOutLinearInInterpolator)
+                .setDuration(mConfig.taskViewExitToHomeDuration)
                 .withLayer()
                 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
                 .start();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0cb2ab9..b941657 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1104,7 +1104,7 @@
             final UserInfo user;
             synchronized (mPackagesLock) {
                 user = mUsers.get(userHandle);
-                if (userHandle == 0 || user == null) {
+                if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                     return false;
                 }
                 mRemovingUserIds.put(userHandle, true);