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 
17 package com.android.settings.bluetooth;
18 
19 import android.app.Service;
20 import android.bluetooth.BluetoothA2dp;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHeadset;
24 import android.bluetooth.BluetoothProfile;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.os.PowerManager;
29 import android.util.Log;
30 
31 public 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
onReceive(Context context, Intent intent)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      */
beginStartingService(Context context, Intent intent)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      */
finishStartingService(Service service, int startId)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