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 android.app.Service;
20import android.bluetooth.BluetoothA2dp;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothDevice;
23import android.bluetooth.BluetoothHeadset;
24import android.bluetooth.BluetoothProfile;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.os.PowerManager;
29import android.util.Log;
30
31public final class DockEventReceiver extends BroadcastReceiver {
32
33    private static final boolean DEBUG = DockService.DEBUG;
34
35    private static final String TAG = "DockEventReceiver";
36
37    public static final String ACTION_DOCK_SHOW_UI =
38        "com.android.settings.bluetooth.action.DOCK_SHOW_UI";
39
40    private static final int EXTRA_INVALID = -1234;
41
42    private static final Object sStartingServiceSync = new Object();
43
44    private static PowerManager.WakeLock sStartingService;
45
46    @Override
47    public void onReceive(Context context, Intent intent) {
48        if (intent == null)
49            return;
50
51        int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, intent.getIntExtra(
52                BluetoothAdapter.EXTRA_STATE, EXTRA_INVALID));
53        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
54
55        if (DEBUG) {
56            Log.d(TAG, "Action: " + intent.getAction() + " State:" + state + " Device: "
57                    + (device == null ? "null" : device.getAliasName()));
58        }
59
60        if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())
61                || ACTION_DOCK_SHOW_UI.endsWith(intent.getAction())) {
62            if ((device == null) && (ACTION_DOCK_SHOW_UI.endsWith(intent.getAction()) ||
63                    ((state != Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
64                     (state != Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
65                if (DEBUG) Log.d(TAG,
66                        "Wrong state: "+state+" or intent: "+intent.toString()+" with null device");
67                return;
68            }
69
70            switch (state) {
71                case Intent.EXTRA_DOCK_STATE_UNDOCKED:
72                case Intent.EXTRA_DOCK_STATE_CAR:
73                case Intent.EXTRA_DOCK_STATE_DESK:
74                case Intent.EXTRA_DOCK_STATE_LE_DESK:
75                case Intent.EXTRA_DOCK_STATE_HE_DESK:
76                    Intent i = new Intent(intent);
77                    i.setClass(context, DockService.class);
78                    beginStartingService(context, i);
79                    break;
80                default:
81                    Log.e(TAG, "Unknown state: " + state);
82                    break;
83            }
84        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction()) ||
85                   BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
86            int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
87                    BluetoothProfile.STATE_CONNECTED);
88            int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
89
90            /*
91             *  Reconnect to the dock if:
92             *  1) it is a dock
93             *  2) it is disconnected
94             *  3) the disconnect is initiated remotely
95             *  4) the dock is still docked (check can only be done in the Service)
96             */
97            if (device == null) {
98                if (DEBUG) Log.d(TAG, "Device is missing");
99                return;
100            }
101
102            if (newState == BluetoothProfile.STATE_DISCONNECTED &&
103                    oldState != BluetoothProfile.STATE_DISCONNECTING) {
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
110        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
111            int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
112            if (btState != BluetoothAdapter.STATE_TURNING_ON) {
113                Intent i = new Intent(intent);
114                i.setClass(context, DockService.class);
115                beginStartingService(context, i);
116            }
117        }
118    }
119
120    /**
121     * Start the service to process the current event notifications, acquiring
122     * the wake lock before returning to ensure that the service will run.
123     */
124    private static void beginStartingService(Context context, Intent intent) {
125        synchronized (sStartingServiceSync) {
126            if (sStartingService == null) {
127                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
128                sStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
129                        "StartingDockService");
130            }
131
132            sStartingService.acquire();
133
134            if (context.startService(intent) == null) {
135                Log.e(TAG, "Can't start DockService");
136            }
137        }
138    }
139
140    /**
141     * Called back by the service when it has finished processing notifications,
142     * releasing the wake lock if the service is now stopping.
143     */
144    public static void finishStartingService(Service service, int startId) {
145        synchronized (sStartingServiceSync) {
146            if (sStartingService != null) {
147                if (DEBUG) Log.d(TAG, "stopSelf id = " + startId);
148                if (service.stopSelfResult(startId)) {
149                    Log.d(TAG, "finishStartingService: stopping service");
150                    sStartingService.release();
151                }
152            }
153        }
154    }
155}
156