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) {
63                if (DEBUG) Log.d(TAG, "Device is missing");
64                return;
65            }
66
67            switch (state) {
68                case Intent.EXTRA_DOCK_STATE_UNDOCKED:
69                case Intent.EXTRA_DOCK_STATE_CAR:
70                case Intent.EXTRA_DOCK_STATE_DESK:
71                case Intent.EXTRA_DOCK_STATE_LE_DESK:
72                case Intent.EXTRA_DOCK_STATE_HE_DESK:
73                    Intent i = new Intent(intent);
74                    i.setClass(context, DockService.class);
75                    beginStartingService(context, i);
76                    break;
77                default:
78                    Log.e(TAG, "Unknown state: " + state);
79                    break;
80            }
81        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction()) ||
82                   BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
83            int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
84                    BluetoothProfile.STATE_CONNECTED);
85            int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
86
87            /*
88             *  Reconnect to the dock if:
89             *  1) it is a dock
90             *  2) it is disconnected
91             *  3) the disconnect is initiated remotely
92             *  4) the dock is still docked (check can only be done in the Service)
93             */
94            if (device == null) {
95                if (DEBUG) Log.d(TAG, "Device is missing");
96                return;
97            }
98
99            if (newState == BluetoothProfile.STATE_DISCONNECTED &&
100                    oldState != BluetoothProfile.STATE_DISCONNECTING) {
101                // Too bad, the dock state can't be checked from a BroadcastReceiver.
102                Intent i = new Intent(intent);
103                i.setClass(context, DockService.class);
104                beginStartingService(context, i);
105            }
106
107        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
108            int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
109            if (btState != BluetoothAdapter.STATE_TURNING_ON) {
110                Intent i = new Intent(intent);
111                i.setClass(context, DockService.class);
112                beginStartingService(context, i);
113            }
114        }
115    }
116
117    /**
118     * Start the service to process the current event notifications, acquiring
119     * the wake lock before returning to ensure that the service will run.
120     */
121    private static void beginStartingService(Context context, Intent intent) {
122        synchronized (sStartingServiceSync) {
123            if (sStartingService == null) {
124                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
125                sStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
126                        "StartingDockService");
127            }
128
129            sStartingService.acquire();
130
131            if (context.startService(intent) == null) {
132                Log.e(TAG, "Can't start DockService");
133            }
134        }
135    }
136
137    /**
138     * Called back by the service when it has finished processing notifications,
139     * releasing the wake lock if the service is now stopping.
140     */
141    public static void finishStartingService(Service service, int startId) {
142        synchronized (sStartingServiceSync) {
143            if (sStartingService != null) {
144                if (DEBUG) Log.d(TAG, "stopSelf id = " + startId);
145                if (service.stopSelfResult(startId)) {
146                    Log.d(TAG, "finishStartingService: stopping service");
147                    sStartingService.release();
148                }
149            }
150        }
151    }
152}
153