1df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan/*
2df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * Copyright (C) 2009 The Android Open Source Project
3df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan *
4df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * Licensed under the Apache License, Version 2.0 (the "License");
5df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * you may not use this file except in compliance with the License.
6df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * You may obtain a copy of the License at
7df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan *
8df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan *      http://www.apache.org/licenses/LICENSE-2.0
9df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan *
10df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * Unless required by applicable law or agreed to in writing, software
11df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * distributed under the License is distributed on an "AS IS" BASIS,
12df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * See the License for the specific language governing permissions and
14df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan * limitations under the License.
15df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan */
16df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
17df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanpackage com.android.settings.bluetooth;
18df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
19df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.app.Service;
20732c1dad5a4871597652f17986b7a602897e5a76Michael Chanimport android.bluetooth.BluetoothA2dp;
21fb5b54d77aee1b2d6a0c0ffe7d47c73a204dee28Michael Chanimport android.bluetooth.BluetoothAdapter;
22df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.bluetooth.BluetoothDevice;
23732c1dad5a4871597652f17986b7a602897e5a76Michael Chanimport android.bluetooth.BluetoothHeadset;
245809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganeshimport android.bluetooth.BluetoothProfile;
25df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.content.BroadcastReceiver;
26df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.content.Context;
27df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.content.Intent;
28df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.os.PowerManager;
29df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chanimport android.util.Log;
30df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
31436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hambypublic final class DockEventReceiver extends BroadcastReceiver {
32df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
33845e740fc63657438b9085376c8e7d60d8334a72Michael Chan    private static final boolean DEBUG = DockService.DEBUG;
34df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
35df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    private static final String TAG = "DockEventReceiver";
36df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
37df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    public static final String ACTION_DOCK_SHOW_UI =
38df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        "com.android.settings.bluetooth.action.DOCK_SHOW_UI";
39df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
40df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    private static final int EXTRA_INVALID = -1234;
41df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
42436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static final Object sStartingServiceSync = new Object();
43df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
44436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static PowerManager.WakeLock sStartingService;
45df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
46df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    @Override
47df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    public void onReceive(Context context, Intent intent) {
48df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        if (intent == null)
49df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            return;
50df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
513fe86a346cdcd41fd9498591571d8b512a130240Michael Chan        int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, intent.getIntExtra(
523fe86a346cdcd41fd9498591571d8b512a130240Michael Chan                BluetoothAdapter.EXTRA_STATE, EXTRA_INVALID));
53df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
54df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
55df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        if (DEBUG) {
56df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            Log.d(TAG, "Action: " + intent.getAction() + " State:" + state + " Device: "
572036ebd8896bbabbbe04db34c9e7d8a1be6fe32aMatthew Xie                    + (device == null ? "null" : device.getAliasName()));
58df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        }
59df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
60df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())
61df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                || ACTION_DOCK_SHOW_UI.endsWith(intent.getAction())) {
62f892bc856c6780187db62681d59ca538a173590fEric Laurent            if ((device == null) && (ACTION_DOCK_SHOW_UI.endsWith(intent.getAction()) ||
63f892bc856c6780187db62681d59ca538a173590fEric Laurent                    ((state != Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
64f892bc856c6780187db62681d59ca538a173590fEric Laurent                     (state != Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
65f892bc856c6780187db62681d59ca538a173590fEric Laurent                if (DEBUG) Log.d(TAG,
66f892bc856c6780187db62681d59ca538a173590fEric Laurent                        "Wrong state: "+state+" or intent: "+intent.toString()+" with null device");
67df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                return;
68df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
69df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
70df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            switch (state) {
71df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                case Intent.EXTRA_DOCK_STATE_UNDOCKED:
72df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                case Intent.EXTRA_DOCK_STATE_CAR:
73df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                case Intent.EXTRA_DOCK_STATE_DESK:
74b8dd637887606242c5cbeaa2fa952946f9013d8fJeff Brown                case Intent.EXTRA_DOCK_STATE_LE_DESK:
75b8dd637887606242c5cbeaa2fa952946f9013d8fJeff Brown                case Intent.EXTRA_DOCK_STATE_HE_DESK:
76df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    Intent i = new Intent(intent);
77df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    i.setClass(context, DockService.class);
78df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    beginStartingService(context, i);
79df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    break;
80df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                default:
81436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    Log.e(TAG, "Unknown state: " + state);
82df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                    break;
83df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
845809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction()) ||
855809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh                   BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
865809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh            int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
875809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh                    BluetoothProfile.STATE_CONNECTED);
885809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh            int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
895809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh
90732c1dad5a4871597652f17986b7a602897e5a76Michael Chan            /*
91732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             *  Reconnect to the dock if:
92732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             *  1) it is a dock
93732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             *  2) it is disconnected
94732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             *  3) the disconnect is initiated remotely
95732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             *  4) the dock is still docked (check can only be done in the Service)
96732c1dad5a4871597652f17986b7a602897e5a76Michael Chan             */
97732c1dad5a4871597652f17986b7a602897e5a76Michael Chan            if (device == null) {
98732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                if (DEBUG) Log.d(TAG, "Device is missing");
99732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                return;
100732c1dad5a4871597652f17986b7a602897e5a76Michael Chan            }
101732c1dad5a4871597652f17986b7a602897e5a76Michael Chan
1025809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh            if (newState == BluetoothProfile.STATE_DISCONNECTED &&
1035809d33296d60f54c7e93de2f45bd84579f70449Jaikumar Ganesh                    oldState != BluetoothProfile.STATE_DISCONNECTING) {
104732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                // Too bad, the dock state can't be checked from a BroadcastReceiver.
105732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                Intent i = new Intent(intent);
106732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                i.setClass(context, DockService.class);
107732c1dad5a4871597652f17986b7a602897e5a76Michael Chan                beginStartingService(context, i);
108732c1dad5a4871597652f17986b7a602897e5a76Michael Chan            }
109732c1dad5a4871597652f17986b7a602897e5a76Michael Chan
110845e740fc63657438b9085376c8e7d60d8334a72Michael Chan        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
111845e740fc63657438b9085376c8e7d60d8334a72Michael Chan            int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
1123fe86a346cdcd41fd9498591571d8b512a130240Michael Chan            if (btState != BluetoothAdapter.STATE_TURNING_ON) {
113845e740fc63657438b9085376c8e7d60d8334a72Michael Chan                Intent i = new Intent(intent);
114845e740fc63657438b9085376c8e7d60d8334a72Michael Chan                i.setClass(context, DockService.class);
115845e740fc63657438b9085376c8e7d60d8334a72Michael Chan                beginStartingService(context, i);
116845e740fc63657438b9085376c8e7d60d8334a72Michael Chan            }
117df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        }
118df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    }
119df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
120df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    /**
121df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     * Start the service to process the current event notifications, acquiring
122df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     * the wake lock before returning to ensure that the service will run.
123df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     */
124436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby    private static void beginStartingService(Context context, Intent intent) {
125436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        synchronized (sStartingServiceSync) {
126436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (sStartingService == null) {
127df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
128436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                sStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
129df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                        "StartingDockService");
130df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
131df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
132436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            sStartingService.acquire();
133df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
134df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            if (context.startService(intent) == null) {
135df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan                Log.e(TAG, "Can't start DockService");
136df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
137df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        }
138df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    }
139df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan
140df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    /**
141df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     * Called back by the service when it has finished processing notifications,
142df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     * releasing the wake lock if the service is now stopping.
143df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan     */
144df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    public static void finishStartingService(Service service, int startId) {
145436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby        synchronized (sStartingServiceSync) {
146436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby            if (sStartingService != null) {
147436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (DEBUG) Log.d(TAG, "stopSelf id = " + startId);
148436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                if (service.stopSelfResult(startId)) {
149436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    Log.d(TAG, "finishStartingService: stopping service");
150436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                    sStartingService.release();
151436b29e68e6608bed9e8e7d54385b8f62d89208eJake Hamby                }
152df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan            }
153df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan        }
154df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan    }
155df9504ef58e8dafdd80ca9cd780510f9444943e2Michael Chan}
156