1436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby/*
2436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * Copyright (C) 2011 The Android Open Source Project
3436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby *
4436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * Licensed under the Apache License, Version 2.0 (the "License");
5436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * you may not use this file except in compliance with the License.
6436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * You may obtain a copy of the License at
7436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby *
8436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby *      http://www.apache.org/licenses/LICENSE-2.0
9436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby *
10436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * Unless required by applicable law or agreed to in writing, software
11436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * distributed under the License is distributed on an "AS IS" BASIS,
12436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * See the License for the specific language governing permissions and
14436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * limitations under the License.
15436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby */
16436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
17436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambypackage com.android.settings.bluetooth;
18436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
19436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport com.android.settings.R;
20436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
21436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.bluetooth.BluetoothAdapter;
22436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.bluetooth.BluetoothClass;
23436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.bluetooth.BluetoothDevice;
24436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.content.BroadcastReceiver;
25436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.content.Context;
26436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.content.Intent;
27436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.content.IntentFilter;
28436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport android.util.Log;
29436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
30436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.ArrayList;
31436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Collection;
32436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.HashMap;
33436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Map;
34436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyimport java.util.Set;
35436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
36436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby/**
37436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
38436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * API and dispatches the event on the UI thread to the right class in the
39436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby * Settings.
40436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby */
41436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambyfinal class BluetoothEventManager {
42436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static final String TAG = "BluetoothEventManager";
43436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
44436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final LocalBluetoothAdapter mLocalAdapter;
45436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final CachedBluetoothDeviceManager mDeviceManager;
46436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private LocalBluetoothProfileManager mProfileManager;
476eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh    private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
48436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final Map<String, Handler> mHandlerMap;
496eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh    private Context mContext;
50436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
51436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final Collection<BluetoothCallback> mCallbacks =
52436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            new ArrayList<BluetoothCallback>();
53436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
54436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    interface Handler {
55436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        void onReceive(Context context, Intent intent, BluetoothDevice device);
56436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
57436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
58436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void addHandler(String action, Handler handler) {
59436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mHandlerMap.put(action, handler);
606eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mAdapterIntentFilter.addAction(action);
616eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh    }
626eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh
636eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh    void addProfileHandler(String action, Handler handler) {
646eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mHandlerMap.put(action, handler);
656eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mProfileIntentFilter.addAction(action);
66436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
67436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
68436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // Set profile manager after construction due to circular dependency
69436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void setProfileManager(LocalBluetoothProfileManager manager) {
70436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mProfileManager = manager;
71436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
72436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
73436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    BluetoothEventManager(LocalBluetoothAdapter adapter,
746eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh            CachedBluetoothDeviceManager deviceManager, Context context) {
75436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mLocalAdapter = adapter;
76436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mDeviceManager = deviceManager;
776eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mAdapterIntentFilter = new IntentFilter();
786eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mProfileIntentFilter = new IntentFilter();
79436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        mHandlerMap = new HashMap<String, Handler>();
806eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mContext = context;
81436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
82436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // Bluetooth on/off broadcasts
83436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
84436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
85436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // Discovery broadcasts
86436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
87436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
88436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
89436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
90436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
91436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
92436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // Pairing broadcasts
93436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
94436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
95436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
96436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // Fine-grained state broadcasts
97436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
98436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
99436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
100436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // Dock event broadcasts
101436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
1026eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
103436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
104436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
1056eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh    void registerProfileIntentReceiver() {
1066eb84ac6de9eeacc1e199d74310cf91f5a98daf2Jaikumar Ganesh        mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter);
107436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
108436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
109436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    /** Register to start receiving callbacks for Bluetooth events. */
110436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void registerCallback(BluetoothCallback callback) {
111436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        synchronized (mCallbacks) {
112436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mCallbacks.add(callback);
113436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
114436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
115436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
116436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    /** Unregister to stop receiving callbacks for Bluetooth events. */
117436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    void unregisterCallback(BluetoothCallback callback) {
118436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        synchronized (mCallbacks) {
119436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mCallbacks.remove(callback);
120436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
121436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
122436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
123436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    // This can't be called from a broadcast receiver where the filter is set in the Manifest.
124436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static String getDockedDeviceAddress(Context context) {
125436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        // This works only because these broadcast intents are "sticky"
126436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
127436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (i != null) {
128436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
129436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
130436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
131436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (device != null) {
132436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    return device.getAddress();
133436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
134436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
135436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
136436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return null;
137436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
138436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
139436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
140436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        @Override
141436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent) {
142436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Log.v(TAG, "Received " + intent.getAction());
143436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
144436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            String action = intent.getAction();
145436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            BluetoothDevice device = intent
146436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
147436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
148436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Handler handler = mHandlerMap.get(action);
149436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (handler != null) {
150436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                handler.onReceive(context, intent, device);
151436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
152436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
153436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    };
154436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
155436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class AdapterStateChangedHandler implements Handler {
156436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
157436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
158436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
159436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                                    BluetoothAdapter.ERROR);
160436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            // update local profiles and get paired devices
161436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mLocalAdapter.setBluetoothStateInt(state);
162436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            // send callback to update UI and possibly start scanning
163436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            synchronized (mCallbacks) {
164436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                for (BluetoothCallback callback : mCallbacks) {
165436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    callback.onBluetoothStateChanged(state);
166436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
167436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
168436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
169436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
170436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
171436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class ScanningStateChangedHandler implements Handler {
172436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        private final boolean mStarted;
173436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
174436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        ScanningStateChangedHandler(boolean started) {
175436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mStarted = started;
176436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
177436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
178436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
179436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            synchronized (mCallbacks) {
180436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                for (BluetoothCallback callback : mCallbacks) {
181436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    callback.onScanningStateChanged(mStarted);
182436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
183436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
184436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mDeviceManager.onScanningStateChanged(mStarted);
185436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
186436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
187436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
188436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
189436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class DeviceFoundHandler implements Handler {
190436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
191436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
192436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
193436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
194436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
195436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            // TODO Pick up UUID. They should be available for 2.1 devices.
196436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
197436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
198436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (cachedDevice == null) {
199436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
200436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
201436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                        + cachedDevice);
202436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                // callback to UI to create Preference for new device
203436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                dispatchDeviceAdded(cachedDevice);
204436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
205436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            cachedDevice.setRssi(rssi);
206436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            cachedDevice.setBtClass(btClass);
207436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            cachedDevice.setName(name);
208436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            cachedDevice.setVisible(true);
209436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
210436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
211436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
212436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
213436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        synchronized (mCallbacks) {
214436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            for (BluetoothCallback callback : mCallbacks) {
215436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                callback.onDeviceAdded(cachedDevice);
216436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
217436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
218436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
219436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
220436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class DeviceDisappearedHandler implements Handler {
221436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
222436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
223436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
224436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (cachedDevice == null) {
225436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
226436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                return;
227436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
22879be0b3e6b5639c4cbe2bbcd9adb0ec4c28716edJake Hamby            if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
229436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                synchronized (mCallbacks) {
230436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    for (BluetoothCallback callback : mCallbacks) {
231436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                        callback.onDeviceDeleted(cachedDevice);
232436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    }
233436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
234436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
235436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
236436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
237436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
238436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class NameChangedHandler implements Handler {
239436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
240436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
241436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mDeviceManager.onDeviceNameUpdated(device);
242436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
243436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
244436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
245436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class BondStateChangedHandler implements Handler {
246436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
247436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
248436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (device == null) {
249436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
250436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                return;
251436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
252436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
253436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                                               BluetoothDevice.ERROR);
254436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
255436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (cachedDevice == null) {
256436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.w(TAG, "CachedBluetoothDevice for device " + device +
257436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                        " not found, calling readPairedDevices().");
258436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (!readPairedDevices()) {
259436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    Log.e(TAG, "Got bonding state changed for " + device +
260436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                            ", but we have no record of that device.");
261436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    return;
262436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
263436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                cachedDevice = mDeviceManager.findDevice(device);
264436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (cachedDevice == null) {
265436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    Log.e(TAG, "Got bonding state changed for " + device +
266436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                            ", but device not added in cache.");
267436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    return;
268436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
269436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
270436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
271436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            synchronized (mCallbacks) {
272436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                for (BluetoothCallback callback : mCallbacks) {
273436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    callback.onDeviceBondStateChanged(cachedDevice, bondState);
274436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
275436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
276436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            cachedDevice.onBondingStateChanged(bondState);
277436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
278436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (bondState == BluetoothDevice.BOND_NONE) {
279436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (device.isBluetoothDock()) {
280436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    // After a dock is unpaired, we will forget the settings
281436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    LocalBluetoothPreferences
282436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                            .removeDockAutoConnectSetting(context, device.getAddress());
283436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
284436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    // if the device is undocked, remove it from the list as well
285436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    if (!device.getAddress().equals(getDockedDeviceAddress(context))) {
28679be0b3e6b5639c4cbe2bbcd9adb0ec4c28716edJake Hamby                        cachedDevice.setVisible(false);
287436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    }
288436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
289436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
290436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                        BluetoothDevice.ERROR);
291436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
292436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                showUnbondMessage(context, cachedDevice.getName(), reason);
293436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
294436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
295436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
296436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        /**
297436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby         * Called when we have reached the unbonded state.
298436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby         *
299436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby         * @param reason one of the error reasons from
300436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby         *            BluetoothDevice.UNBOND_REASON_*
301436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby         */
302436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        private void showUnbondMessage(Context context, String name, int reason) {
303436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int errorMsg;
304436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
305436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            switch(reason) {
306436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
307436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                errorMsg = R.string.bluetooth_pairing_pin_error_message;
308436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                break;
309436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
310436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                errorMsg = R.string.bluetooth_pairing_rejected_error_message;
311436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                break;
312436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
313436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                errorMsg = R.string.bluetooth_pairing_device_down_error_message;
314436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                break;
315436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
316436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
317436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
318436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
319436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                errorMsg = R.string.bluetooth_pairing_error_message;
320436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                break;
321436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            default:
322436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
323436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                return;
324436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
325436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Utils.showError(context, name, errorMsg);
326436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
327436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
328436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
329436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class ClassChangedHandler implements Handler {
330436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
331436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
332436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mDeviceManager.onBtClassChanged(device);
333436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
334436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
335436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
336436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class UuidChangedHandler implements Handler {
337436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent,
338436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                BluetoothDevice device) {
339436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            mDeviceManager.onUuidChanged(device);
340436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
341436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
342436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
343436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class PairingCancelHandler implements Handler {
344436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
345436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (device == null) {
346436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
347436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                return;
348436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
349436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int errorMsg = R.string.bluetooth_pairing_error_message;
350436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
351436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            Utils.showError(context, cachedDevice.getName(), errorMsg);
352436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
353436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
354436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
355436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private class DockEventHandler implements Handler {
356436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
357436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            // Remove if unpair device upon undocking
358436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
359436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
360436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
361436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
362436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
363436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    if (cachedDevice != null) {
36479be0b3e6b5639c4cbe2bbcd9adb0ec4c28716edJake Hamby                        cachedDevice.setVisible(false);
365436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    }
366436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
367436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
368436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
369436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
370436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
371436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    boolean readPairedDevices() {
372436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
373436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        if (bondedDevices == null) {
374436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            return false;
375436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
376436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
377436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        boolean deviceAdded = false;
378436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        for (BluetoothDevice device : bondedDevices) {
379436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
380436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (cachedDevice == null) {
381436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
382436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                dispatchDeviceAdded(cachedDevice);
383436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                deviceAdded = true;
384436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            }
385436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        }
386436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby
387436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        return deviceAdded;
388436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    }
389436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby}
390