1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.bluetooth;
18
19import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
20
21import android.app.Service;
22import android.bluetooth.BluetoothA2dp;
23import android.bluetooth.BluetoothAdapter;
24import android.bluetooth.BluetoothDevice;
25import android.bluetooth.BluetoothHeadset;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.os.PowerManager;
31import android.util.Log;
32
33public class DockEventReceiver extends BroadcastReceiver {
34
35    private static final boolean DEBUG = DockService.DEBUG;
36
37    private static final String TAG = "DockEventReceiver";
38
39    public static final String ACTION_DOCK_SHOW_UI =
40        "com.android.settings.bluetooth.action.DOCK_SHOW_UI";
41
42    private static final int EXTRA_INVALID = -1234;
43
44    private static final Object mStartingServiceSync = new Object();
45
46    private static final long WAKELOCK_TIMEOUT = 5000;
47
48    private static PowerManager.WakeLock mStartingService;
49
50    @Override
51    public void onReceive(Context context, Intent intent) {
52        if (intent == null)
53            return;
54
55        int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, intent.getIntExtra(
56                BluetoothAdapter.EXTRA_STATE, EXTRA_INVALID));
57        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
58
59        if (DEBUG) {
60            Log.d(TAG, "Action: " + intent.getAction() + " State:" + state + " Device: "
61                    + (device == null ? "null" : device.getName()));
62        }
63
64        if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())
65                || ACTION_DOCK_SHOW_UI.endsWith(intent.getAction())) {
66            if (device == null) {
67                if (DEBUG) Log.d(TAG, "Device is missing");
68                return;
69            }
70
71            switch (state) {
72                case Intent.EXTRA_DOCK_STATE_UNDOCKED:
73                case Intent.EXTRA_DOCK_STATE_CAR:
74                case Intent.EXTRA_DOCK_STATE_DESK:
75                    Intent i = new Intent(intent);
76                    i.setClass(context, DockService.class);
77                    beginStartingService(context, i);
78                    break;
79                default:
80                    if (DEBUG) Log.e(TAG, "Unknown state");
81                    break;
82            }
83        } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
84            /*
85             *  Reconnect to the dock if:
86             *  1) it is a dock
87             *  2) it is disconnected
88             *  3) the disconnect is initiated remotely
89             *  4) the dock is still docked (check can only be done in the Service)
90             */
91            if (device == null) {
92                if (DEBUG) Log.d(TAG, "Device is missing");
93                return;
94            }
95
96            int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
97                    BluetoothHeadset.STATE_CONNECTED);
98            if (newState != BluetoothHeadset.STATE_DISCONNECTED) return;
99
100            int source = intent.getIntExtra(BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
101                    BluetoothHeadset.LOCAL_DISCONNECT);
102            if (source != BluetoothHeadset.REMOTE_DISCONNECT) return;
103
104            // Too bad, the dock state can't be checked from a BroadcastReceiver.
105            Intent i = new Intent(intent);
106            i.setClass(context, DockService.class);
107            beginStartingService(context, i);
108
109        } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
110            /*
111             *  Reconnect to the dock if:
112             *  1) it is a dock
113             *  2) it is an unexpected disconnect i.e. didn't go through disconnecting state
114             *  3) the dock is still docked (check can only be done in the Service)
115             */
116            if (device == null) {
117                if (DEBUG) Log.d(TAG, "Device is missing");
118                return;
119            }
120
121            int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
122            int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
123            if (newState == BluetoothA2dp.STATE_DISCONNECTED &&
124                    oldState != BluetoothA2dp.STATE_DISCONNECTING) {
125                // Too bad, the dock state can't be checked from a BroadcastReceiver.
126                Intent i = new Intent(intent);
127                i.setClass(context, DockService.class);
128                beginStartingService(context, i);
129            }
130
131        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
132            int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
133            if (btState != BluetoothAdapter.STATE_TURNING_ON) {
134                Intent i = new Intent(intent);
135                i.setClass(context, DockService.class);
136                beginStartingService(context, i);
137            }
138        }
139    }
140
141    /**
142     * Start the service to process the current event notifications, acquiring
143     * the wake lock before returning to ensure that the service will run.
144     */
145    public static void beginStartingService(Context context, Intent intent) {
146        synchronized (mStartingServiceSync) {
147            if (mStartingService == null) {
148                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
149                mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
150                        "StartingDockService");
151            }
152
153            mStartingService.acquire(WAKELOCK_TIMEOUT);
154
155            if (context.startService(intent) == null) {
156                Log.e(TAG, "Can't start DockService");
157            }
158        }
159    }
160
161    /**
162     * Called back by the service when it has finished processing notifications,
163     * releasing the wake lock if the service is now stopping.
164     */
165    public static void finishStartingService(Service service, int startId) {
166        synchronized (mStartingServiceSync) {
167            if (mStartingService != null) {
168                if (DEBUG) Log.d(TAG, "stopSelf id = "+ startId);
169                service.stopSelfResult(startId);
170            }
171        }
172    }
173}
174