19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * TODO: Move this to services.jar
199a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby * and make the constructor package private again.
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.server;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.bluetooth.BluetoothA2dp;
26bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pellyimport android.bluetooth.BluetoothAdapter;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
2896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganeshimport android.bluetooth.BluetoothProfile;
29d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganeshimport android.bluetooth.BluetoothUuid;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.bluetooth.IBluetoothA2dp;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.BroadcastReceiver;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.IntentFilter;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.media.AudioManager;
36b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurentimport android.os.Handler;
37b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurentimport android.os.Message;
389b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganeshimport android.os.ParcelUuid;
39b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurentimport android.os.PowerManager;
40b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurentimport android.os.PowerManager.WakeLock;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.Settings;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileDescriptor;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.PrintWriter;
4696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganeshimport java.util.ArrayList;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
4803cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganeshimport java.util.List;
4996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class BluetoothA2dpService extends IBluetoothA2dp.Stub {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "BluetoothA2dpService";
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final boolean DBG = true;
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String BLUETOOTH_A2DP_SERVICE = "bluetooth_a2dp";
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
62d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    private static final String PROPERTY_STATE = "State";
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
64d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    private final Context mContext;
65d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    private final IntentFilter mIntentFilter;
66bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    private HashMap<BluetoothDevice, Integer> mAudioDevices;
67d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    private final AudioManager mAudioManager;
68bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    private final BluetoothService mBluetoothService;
69bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    private final BluetoothAdapter mAdapter;
70a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent    private int   mTargetA2dpState;
7196a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    private BluetoothDevice mPlayingA2dpDevice;
72b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    private IntentBroadcastHandler mIntentBroadcastHandler;
73b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    private final WakeLock mWakeLock;
74b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
75b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    private static final int MSG_CONNECTION_STATE_CHANGED = 0;
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onReceive(Context context, Intent intent) {
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String action = intent.getAction();
81bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            BluetoothDevice device =
82005b228cdfb369d9b3b325884c0337ba5968bf8cNick Pelly                    intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
83de893f550301a60274e87aa8168225e7a7a42184Nick Pelly            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
84de893f550301a60274e87aa8168225e7a7a42184Nick Pelly                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
85b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly                                               BluetoothAdapter.ERROR);
86105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                switch (state) {
87de893f550301a60274e87aa8168225e7a7a42184Nick Pelly                case BluetoothAdapter.STATE_ON:
88105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    onBluetoothEnable();
89105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    break;
90de893f550301a60274e87aa8168225e7a7a42184Nick Pelly                case BluetoothAdapter.STATE_TURNING_OFF:
91105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    onBluetoothDisable();
92105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    break;
93105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
944c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh            } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
954c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                synchronized (this) {
964c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                    if (mAudioDevices.containsKey(device)) {
974c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                        int state = mAudioDevices.get(device);
984c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                        handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
994c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                    }
1004c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh                }
1014f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1024f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1034f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                if (streamType == AudioManager.STREAM_MUSIC) {
10403cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh                    List<BluetoothDevice> sinks = getConnectedDevices();
10596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
10603cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh                    if (sinks.size() != 0 && isPhoneDocked(sinks.get(0))) {
10703cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh                        String address = sinks.get(0).getAddress();
1084f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        int newVolLevel =
1094f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                          intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1104f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        int oldVolLevel =
1114f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                          intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
1124f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        String path = mBluetoothService.getObjectPathFromAddress(address);
1134f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        if (newVolLevel > oldVolLevel) {
1144f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                            avrcpVolumeUpNative(path);
1154f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        } else if (newVolLevel < oldVolLevel) {
1164f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                            avrcpVolumeDownNative(path);
1174f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                        }
1184f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                    }
1194f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                }
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1244f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh    private boolean isPhoneDocked(BluetoothDevice device) {
1254f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        // This works only because these broadcast intents are "sticky"
1264f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
1274f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        if (i != null) {
1284f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh            int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
1294f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh            if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1304f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                BluetoothDevice dockDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1314f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                if (dockDevice != null && device.equals(dockDevice)) {
1324f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                    return true;
1334f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh                }
1344f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh            }
1354f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        }
1364f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        return false;
1374f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh    }
1384f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh
139bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    public BluetoothA2dpService(Context context, BluetoothService bluetoothService) {
140d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        mContext = context;
141d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
142b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
143b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService");
144b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
145b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        mIntentBroadcastHandler = new IntentBroadcastHandler();
146b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
147d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
148d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
149d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        mBluetoothService = bluetoothService;
150d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (mBluetoothService == null) {
151d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            throw new RuntimeException("Platform does not support Bluetooth");
152d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        }
153d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
154d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (!initNative()) {
155d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            throw new RuntimeException("Could not init BluetoothA2dpService");
156d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        }
157d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
158f242b7b931898856bcbcb7ec36cacf43098ba544Nick Pelly        mAdapter = BluetoothAdapter.getDefaultAdapter();
159bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly
160de893f550301a60274e87aa8168225e7a7a42184Nick Pelly        mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
161005b228cdfb369d9b3b325884c0337ba5968bf8cNick Pelly        mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
1624c9a29efdbf58034c1d90eb001dc7299f1f012eaJaikumar Ganesh        mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
1634f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh        mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
164d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        mContext.registerReceiver(mReceiver, mIntentFilter);
165d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
166bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        mAudioDevices = new HashMap<BluetoothDevice, Integer>();
167d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
168d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (mBluetoothService.isEnabled())
169d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            onBluetoothEnable();
170a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        mTargetA2dpState = -1;
1719b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        mBluetoothService.setA2dpService(this);
172d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    }
173d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
174d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    @Override
175d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    protected void finalize() throws Throwable {
176d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        try {
177d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            cleanupNative();
178d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        } finally {
179d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            super.finalize();
180d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        }
181d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    }
182d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
1839a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby    private int convertBluezSinkStringToState(String value) {
184d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (value.equalsIgnoreCase("disconnected"))
185d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            return BluetoothA2dp.STATE_DISCONNECTED;
186d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (value.equalsIgnoreCase("connecting"))
187d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            return BluetoothA2dp.STATE_CONNECTING;
188d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (value.equalsIgnoreCase("connected"))
189d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            return BluetoothA2dp.STATE_CONNECTED;
190d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (value.equalsIgnoreCase("playing"))
191d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            return BluetoothA2dp.STATE_PLAYING;
192d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        return -1;
193d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    }
194d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
195bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    private boolean isSinkDevice(BluetoothDevice device) {
196dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh        ParcelUuid[] uuids = mBluetoothService.getRemoteUuids(device.getAddress());
197dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh        if (uuids != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
198dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh            return true;
1997f74d534f39c4d1e1db8604faa117dcf127cc8b9Jaikumar Ganesh        }
2007f74d534f39c4d1e1db8604faa117dcf127cc8b9Jaikumar Ganesh        return false;
2017f74d534f39c4d1e1db8604faa117dcf127cc8b9Jaikumar Ganesh    }
2027f74d534f39c4d1e1db8604faa117dcf127cc8b9Jaikumar Ganesh
2038594394aadeaaa3f834d33d7b198fb071e0f31feMatthew Xie    private synchronized void addAudioSink(BluetoothDevice device) {
2048594394aadeaaa3f834d33d7b198fb071e0f31feMatthew Xie        if (mAudioDevices.get(device) == null) {
2058594394aadeaaa3f834d33d7b198fb071e0f31feMatthew Xie            mAudioDevices.put(device, BluetoothA2dp.STATE_DISCONNECTED);
206d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        }
207d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    }
208d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private synchronized void onBluetoothEnable() {
210a66590ed78d0ec8e837dab80a4376a10d7023e9aJaikumar Ganesh        String devices = mBluetoothService.getProperty("Devices", true);
211d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (devices != null) {
212d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            String [] paths = devices.split(",");
213d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            for (String path: paths) {
214d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh                String address = mBluetoothService.getAddressFromObjectPath(path);
215bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                BluetoothDevice device = mAdapter.getRemoteDevice(address);
216dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                ParcelUuid[] remoteUuids = mBluetoothService.getRemoteUuids(address);
217dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                if (remoteUuids != null)
218dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                    if (BluetoothUuid.containsAnyUuid(remoteUuids,
219dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                            new ParcelUuid[] {BluetoothUuid.AudioSink,
220dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                                                BluetoothUuid.AdvAudioDist})) {
221dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                        addAudioSink(device);
222d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh                    }
223dd0463aef18d251c741bfc9dc7a2787443ef36f1Jaikumar Ganesh                }
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
225a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent        mAudioManager.setParameters(BLUETOOTH_ENABLED+"=true");
22680a6a229b33b0868944fe011af2d778e23c83322Eric Laurent        mAudioManager.setParameters("A2dpSuspended=false");
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private synchronized void onBluetoothDisable() {
230d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (!mAudioDevices.isEmpty()) {
231bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            BluetoothDevice[] devices = new BluetoothDevice[mAudioDevices.size()];
232bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            devices = mAudioDevices.keySet().toArray(devices);
233bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            for (BluetoothDevice device : devices) {
23496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                int state = getConnectionState(device);
235d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh                switch (state) {
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case BluetoothA2dp.STATE_CONNECTING:
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case BluetoothA2dp.STATE_CONNECTED:
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case BluetoothA2dp.STATE_PLAYING:
239bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                        disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(
240bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                                device.getAddress()));
241bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                        handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case BluetoothA2dp.STATE_DISCONNECTING:
244bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                        handleSinkStateChange(device, BluetoothA2dp.STATE_DISCONNECTING,
245bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                                              BluetoothA2dp.STATE_DISCONNECTED);
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
249d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            mAudioDevices.clear();
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
251a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent
252bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        mAudioManager.setParameters(BLUETOOTH_ENABLED + "=false");
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) {
2569b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        if (!mBluetoothService.isEnabled() || !isSinkDevice(device) ||
25796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                getPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
2589a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby            return false;
2599a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby        }
2609b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
2618594394aadeaaa3f834d33d7b198fb071e0f31feMatthew Xie        addAudioSink(device);
2629b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
2639a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
2649a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby        if (path == null) {
2659a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby            return false;
2669a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby        }
2679a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby        return true;
2689b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    }
2699b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
27096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized boolean isA2dpPlaying(BluetoothDevice device) {
27196a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
27296a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            "Need BLUETOOTH_ADMIN permission");
27396a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        if (DBG) log("isA2dpPlaying(" + device + ")");
27496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        if (device.equals(mPlayingA2dpDevice)) return true;
27596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        return false;
27696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    }
27796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
27896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized boolean connect(BluetoothDevice device) {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                "Need BLUETOOTH_ADMIN permission");
281bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        if (DBG) log("connectSink(" + device + ")");
2829b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        if (!isConnectSinkFeasible(device)) return false;
283d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
28496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        for (BluetoothDevice sinkDevice : mAudioDevices.keySet()) {
28596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            if (getConnectionState(sinkDevice) != BluetoothProfile.STATE_DISCONNECTED) {
28696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                disconnect(sinkDevice);
28796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            }
28896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        }
28996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
2909b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        return mBluetoothService.connectSink(device.getAddress());
2919b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    }
2929b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
2939b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    public synchronized boolean connectSinkInternal(BluetoothDevice device) {
2944aae54cf29c562a6c59ee47732745a224fa2882dJaikumar Ganesh        if (!mBluetoothService.isEnabled()) return false;
2954aae54cf29c562a6c59ee47732745a224fa2882dJaikumar Ganesh
2969b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        int state = mAudioDevices.get(device);
2979b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
298c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        // ignore if there are any active sinks
29996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        if (getDevicesMatchingConnectionStates(new int[] {
300c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                BluetoothA2dp.STATE_CONNECTING,
301c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                BluetoothA2dp.STATE_CONNECTED,
30203cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh                BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
303b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return false;
304c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
305c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (state) {
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case BluetoothA2dp.STATE_CONNECTED:
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case BluetoothA2dp.STATE_DISCONNECTING:
309b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return false;
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case BluetoothA2dp.STATE_CONNECTING:
311b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return true;
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
314bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
315d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
316bcf5cf47ed03f1a685fd1de8ed9dc4a6abf7b1e3Jaikumar Ganesh        // State is DISCONNECTED and we are connecting.
317aa4b235dd720d1f2861681cdfabf3b5fabc61fb0Jaikumar Ganesh        if (getPriority(device) < BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
318aa4b235dd720d1f2861681cdfabf3b5fabc61fb0Jaikumar Ganesh            setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
319bcf5cf47ed03f1a685fd1de8ed9dc4a6abf7b1e3Jaikumar Ganesh        }
320c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        handleSinkStateChange(device, state, BluetoothA2dp.STATE_CONNECTING);
321c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!connectSinkNative(path)) {
323c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            // Restore previous state
324c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            handleSinkStateChange(device, mAudioDevices.get(device), state);
325b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return false;
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
327b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly        return true;
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3309b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    private synchronized boolean isDisconnectSinkFeasible(BluetoothDevice device) {
331bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (path == null) {
333b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return false;
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
335d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh
33696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        int state = getConnectionState(device);
337c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        switch (state) {
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case BluetoothA2dp.STATE_DISCONNECTED:
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case BluetoothA2dp.STATE_DISCONNECTING:
34096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            return false;
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3429b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        return true;
3439b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    }
3449b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
34596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized boolean disconnect(BluetoothDevice device) {
3469b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
3479b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh                                                "Need BLUETOOTH_ADMIN permission");
3489b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        if (DBG) log("disconnectSink(" + device + ")");
3499b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        if (!isDisconnectSinkFeasible(device)) return false;
3509b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        return mBluetoothService.disconnectSink(device.getAddress());
3519b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    }
3529b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
3539b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
35496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        int state = getConnectionState(device);
3559b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3575844f1d35731b619969805957d7c3cce54431785Jaikumar Ganesh        switch (state) {
3585844f1d35731b619969805957d7c3cce54431785Jaikumar Ganesh            case BluetoothA2dp.STATE_DISCONNECTED:
3595844f1d35731b619969805957d7c3cce54431785Jaikumar Ganesh            case BluetoothA2dp.STATE_DISCONNECTING:
3605844f1d35731b619969805957d7c3cce54431785Jaikumar Ganesh                return false;
3615844f1d35731b619969805957d7c3cce54431785Jaikumar Ganesh        }
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // State is CONNECTING or CONNECTED or PLAYING
363c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTING);
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!disconnectSinkNative(path)) {
365c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            // Restore previous state
366c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            handleSinkStateChange(device, mAudioDevices.get(device), state);
367b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly            return false;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
369c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        return true;
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
372f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    public synchronized boolean suspendSink(BluetoothDevice device) {
373f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
374f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan                            "Need BLUETOOTH_ADMIN permission");
375a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        if (DBG) log("suspendSink(" + device + "), mTargetA2dpState: "+mTargetA2dpState);
376f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        if (device == null || mAudioDevices == null) {
377f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan            return false;
378f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        }
379f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
38062895a67d2c34fc2a4b8943e330c4269275b1c20Nick Pelly        Integer state = mAudioDevices.get(device);
38162895a67d2c34fc2a4b8943e330c4269275b1c20Nick Pelly        if (path == null || state == null) {
382f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan            return false;
383f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        }
384a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent
385a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        mTargetA2dpState = BluetoothA2dp.STATE_CONNECTED;
386a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        return checkSinkSuspendState(state.intValue());
387f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    }
388f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan
389f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    public synchronized boolean resumeSink(BluetoothDevice device) {
390f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
391f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan                            "Need BLUETOOTH_ADMIN permission");
392a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        if (DBG) log("resumeSink(" + device + "), mTargetA2dpState: "+mTargetA2dpState);
393f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        if (device == null || mAudioDevices == null) {
394f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan            return false;
395f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        }
396f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
39762895a67d2c34fc2a4b8943e330c4269275b1c20Nick Pelly        Integer state = mAudioDevices.get(device);
39862895a67d2c34fc2a4b8943e330c4269275b1c20Nick Pelly        if (path == null || state == null) {
399f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan            return false;
400f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan        }
401a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        mTargetA2dpState = BluetoothA2dp.STATE_PLAYING;
402a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        return checkSinkSuspendState(state.intValue());
403f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    }
404f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan
40596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized int getConnectionState(BluetoothDevice device) {
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
40796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        Integer state = mAudioDevices.get(device);
40896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        if (state == null)
40996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            return BluetoothA2dp.STATE_DISCONNECTED;
41096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        return state;
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
41303cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh    public synchronized List<BluetoothDevice> getConnectedDevices() {
414b16c4f7dd92dabf0cc27438a5d3d9ebd203a38b3Jaikumar Ganesh        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
41503cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh        List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates(
41696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                new int[] {BluetoothA2dp.STATE_CONNECTED});
41796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        return sinks;
418b16c4f7dd92dabf0cc27438a5d3d9ebd203a38b3Jaikumar Ganesh    }
419b16c4f7dd92dabf0cc27438a5d3d9ebd203a38b3Jaikumar Ganesh
42003cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh    public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
42203cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh        ArrayList<BluetoothDevice> sinks = new ArrayList<BluetoothDevice>();
42396a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        for (BluetoothDevice device: mAudioDevices.keySet()) {
42496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            int sinkState = getConnectionState(device);
42596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            for (int state : states) {
42696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                if (state == sinkState) {
42796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                    sinks.add(device);
42896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                    break;
42996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                }
43096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            }
43196a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        }
43203cd78cf5e51c3adb78d2e3d314838dcf3e36b26Jaikumar Ganesh        return sinks;
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
43596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized int getPriority(BluetoothDevice device) {
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Settings.Secure.getInt(mContext.getContentResolver(),
438bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
439ccaebfc9428aa7c167caf469f7393e1fb375307eJaikumar Ganesh                BluetoothA2dp.PRIORITY_UNDEFINED);
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
44296a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    public synchronized boolean setPriority(BluetoothDevice device, int priority) {
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                "Need BLUETOOTH_ADMIN permission");
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Settings.Secure.putInt(mContext.getContentResolver(),
446b24e11baac589fe16426f2d243b460ab84991c7bNick Pelly                Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
449a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie    public synchronized boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
450a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
451a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie                                                "Need BLUETOOTH_ADMIN permission");
452a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        String address = device.getAddress();
453a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
454a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie            return false;
455a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        }
456a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        Integer data = mBluetoothService.getAuthorizationAgentRequestData(address);
457a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        if (data == null) {
458a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie            Log.w(TAG, "allowIncomingConnect(" + device + ") called but no native data available");
459a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie            return false;
460a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        }
461a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        log("allowIncomingConnect: A2DP: " + device + ":" + value);
462a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie        return mBluetoothService.setAuthorizationNative(address, value, data.intValue());
463a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie    }
464a0c680393f2dd03a937c598b2cb9abf98a58152cMatthew Xie
4659a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby    /**
4669a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * Called by native code on a PropertyChanged signal from
4679a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * org.bluez.AudioSink.
4689a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     *
4699a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * @param path the object path for the changed device
4709a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * @param propValues a string array containing the key and one or more
4719a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     *  values.
4729a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     */
4739a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby    private synchronized void onSinkPropertyChanged(String path, String[] propValues) {
4749488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh        if (!mBluetoothService.isEnabled()) {
4759488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh            return;
4769488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh        }
4779488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh
478d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        String name = propValues[0];
479d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        String address = mBluetoothService.getAddressFromObjectPath(path);
480d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (address == null) {
481d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null");
482d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh            return;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
485bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        BluetoothDevice device = mAdapter.getRemoteDevice(address);
486bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly
487d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (name.equals(PROPERTY_STATE)) {
4889a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby            int state = convertBluezSinkStringToState(propValues[1]);
4898febf8861857b284721ffe6caaedbd495a87e73fJaikumar Ganesh            log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
4908febf8861857b284721ffe6caaedbd495a87e73fJaikumar Ganesh
491bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            if (mAudioDevices.get(device) == null) {
4929488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh                // This is for an incoming connection for a device not known to us.
4939488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh                // We have authorized it and bluez state has changed.
494bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly                addAudioSink(device);
4958594394aadeaaa3f834d33d7b198fb071e0f31feMatthew Xie                handleSinkStateChange(device, BluetoothA2dp.STATE_DISCONNECTED, state);
4969488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh            } else {
49796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                if (state == BluetoothA2dp.STATE_PLAYING && mPlayingA2dpDevice == null) {
49896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                   mPlayingA2dpDevice = device;
49996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                   handleSinkPlayingStateChange(device, state, BluetoothA2dp.STATE_NOT_PLAYING);
50096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                } else if (state == BluetoothA2dp.STATE_CONNECTED && mPlayingA2dpDevice != null) {
50196a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                    mPlayingA2dpDevice = null;
50296a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                    handleSinkPlayingStateChange(device, BluetoothA2dp.STATE_NOT_PLAYING,
50396a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                        BluetoothA2dp.STATE_PLAYING);
50496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                } else {
505b2ec4fa8d7ee957ddb2c85ce2ed74fdaafe144e9Jaikumar Ganesh                   mPlayingA2dpDevice = null;
50696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                   int prevState = mAudioDevices.get(device);
50796a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                   handleSinkStateChange(device, prevState, state);
50896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                }
5099488cbd0b9ce0a9b49647910e76c6fa910f948eaJaikumar Ganesh            }
510a0de355ce04eb29356ba6f171dcf093a271f221cMike Lockwood        }
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
513bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly    private void handleSinkStateChange(BluetoothDevice device, int prevState, int state) {
514d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (state != prevState) {
515bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            mAudioDevices.put(device, state);
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
517a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            checkSinkSuspendState(state);
518a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            mTargetA2dpState = -1;
519a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent
52096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh            if (getPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
521721361f1a74b876c42cc0533c9c19320e8c09927Jaikumar Ganesh                    state == BluetoothA2dp.STATE_CONNECTED) {
522721361f1a74b876c42cc0533c9c19320e8c09927Jaikumar Ganesh                // We have connected or attempting to connect.
523721361f1a74b876c42cc0533c9c19320e8c09927Jaikumar Ganesh                // Bump priority
52496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
5259b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh                // We will only have 1 device with AUTO_CONNECT priority
5269b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh                // To be backward compatible set everyone else to have PRIORITY_ON
5279b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh                adjustOtherSinkPriorities(device);
5289b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh            }
5299b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
530b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, state);
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
532b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            mWakeLock.acquire();
533b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
534b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                            MSG_CONNECTION_STATE_CHANGED,
535b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                            prevState,
536b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                            state,
537b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                            device),
538b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                       delay);
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
54296a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    private void handleSinkPlayingStateChange(BluetoothDevice device, int state, int prevState) {
54396a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
54496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
54596a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
54696a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
54724ca01d262f0e8563b09b3c7b4574a2bb8036f12Jake Hamby        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
54896a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
54996a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
55096a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh        if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
55196a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh    }
55296a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh
5539b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
554bcf5cf47ed03f1a685fd1de8ed9dc4a6abf7b1e3Jaikumar Ganesh        for (BluetoothDevice device : mAdapter.getBondedDevices()) {
555aa4b235dd720d1f2861681cdfabf3b5fabc61fb0Jaikumar Ganesh            if (getPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
556bcf5cf47ed03f1a685fd1de8ed9dc4a6abf7b1e3Jaikumar Ganesh                !device.equals(connectedDevice)) {
557aa4b235dd720d1f2861681cdfabf3b5fabc61fb0Jaikumar Ganesh                setPriority(device, BluetoothA2dp.PRIORITY_ON);
5589b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh            }
5599b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh        }
5609b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh    }
5619b637e5985f9a86f39d70335c0390ade3716592aJaikumar Ganesh
562a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent    private boolean checkSinkSuspendState(int state) {
563a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        boolean result = true;
564a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent
565a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        if (state != mTargetA2dpState) {
566a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            if (state == BluetoothA2dp.STATE_PLAYING &&
567a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent                mTargetA2dpState == BluetoothA2dp.STATE_CONNECTED) {
568a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent                mAudioManager.setParameters("A2dpSuspended=true");
569a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            } else if (state == BluetoothA2dp.STATE_CONNECTED &&
570a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent                mTargetA2dpState == BluetoothA2dp.STATE_PLAYING) {
571a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent                mAudioManager.setParameters("A2dpSuspended=false");
572a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            } else {
573a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent                result = false;
574a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent            }
575a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        }
576a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent        return result;
577a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent    }
578a47d153647d8750b667942ad80e7b905ea98c68bEric Laurent
5799a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby    /**
5809a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * Called by native code for the async response to a Connect
5819a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * method call to org.bluez.AudioSink.
5829a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     *
5839a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * @param deviceObjectPath the object path for the connecting device
5849a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     * @param result true on success; false on error
5859a62c9cd6585656f4e29ba971b1f88a510d674bdJake Hamby     */
586c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh    private void onConnectSinkResult(String deviceObjectPath, boolean result) {
587c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        // If the call was a success, ignore we will update the state
588c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        // when we a Sink Property Change
589c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        if (!result) {
590c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            if (deviceObjectPath != null) {
591c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh                String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
592c0cec6245429ef1c9617a710fe08b2999b329d08Jaikumar Ganesh                if (address == null) return;
593c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh                BluetoothDevice device = mAdapter.getRemoteDevice(address);
59496a79830ea1ae3ab3d6d3cce2bd1397fcd40ea0eJaikumar Ganesh                int state = getConnectionState(device);
595c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh                handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
596c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh            }
597c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh        }
598c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh    }
599c0e32f12439e5373dca5879e4eb7e819be7fb417Jaikumar Ganesh
600b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    /** Handles A2DP connection state change intent broadcasts. */
601b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    private class IntentBroadcastHandler extends Handler {
602b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
603b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) {
604b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
605b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
606b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
607b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
608b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
609b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
610b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
611b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
612b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
613b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state,
614b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                                                        prevState);
615b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        }
616b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
617b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        @Override
618b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        public void handleMessage(Message msg) {
619b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            switch (msg.what) {
620b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                case MSG_CONNECTION_STATE_CHANGED:
621b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                    onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
622b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                    mWakeLock.release();
623b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent                    break;
624b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent            }
625b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent        }
626b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent    }
627b1fbaaccb656ef09a8770c28df15e3e91a452e64Eric Laurent
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
630eb4cc492c93ab9635dde78b958a834120412e72aJeff Sharkey        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
631eb4cc492c93ab9635dde78b958a834120412e72aJeff Sharkey
632d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh        if (mAudioDevices.isEmpty()) return;
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        pw.println("Cached audio devices:");
634bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly        for (BluetoothDevice device : mAudioDevices.keySet()) {
635bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            int state = mAudioDevices.get(device);
636bd022f423a33f0794bb53e5b0720da2d67e4631cNick Pelly            pw.println(device + " " + BluetoothA2dp.stateToString(state));
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void log(String msg) {
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.d(TAG, msg);
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native boolean initNative();
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native void cleanupNative();
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private synchronized native boolean connectSinkNative(String path);
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private synchronized native boolean disconnectSinkNative(String path);
648f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    private synchronized native boolean suspendSinkNative(String path);
649f9bbe1e71a502fe7bd1f4a23ba5bbe4dde0d9d57Zhu Lan    private synchronized native boolean resumeSinkNative(String path);
650d5ac1ae36b4e096eb97984334f86d0c68abea2f7Jaikumar Ganesh    private synchronized native Object []getSinkPropertiesNative(String path);
6514f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh    private synchronized native boolean avrcpVolumeUpNative(String path);
6524f773a13043ec22ccd2f9d33ee86a305738c5b23Jaikumar Ganesh    private synchronized native boolean avrcpVolumeDownNative(String path);
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
654