• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 android.bluetooth;
18 
19 import android.os.Handler;
20 import android.os.Message;
21 import android.os.PowerManager;
22 import android.os.PowerManager.WakeLock;
23 import android.util.Log;
24 
25 /**
26  * The Android Bluetooth API is not finalized, and *will* change. Use at your
27  * own risk.
28  *
29  * Simple SCO Socket.
30  * Currently in Android, there is no support for sending data over a SCO
31  * socket - this is managed by the hardware link to the Bluetooth Chip. This
32  * class is instead intended for management of the SCO socket lifetime,
33  * and is tailored for use with the headset / handsfree profiles.
34  * @hide
35  */
36 public class ScoSocket {
37     private static final String TAG = "ScoSocket";
38     private static final boolean DBG = true;
39     private static final boolean VDBG = false;  // even more logging
40 
41     public static final int STATE_READY = 1;    // Ready for use. No threads or sockets
42     public static final int STATE_ACCEPT = 2;   // accept() thread running
43     public static final int STATE_CONNECTING = 3;  // connect() thread running
44     public static final int STATE_CONNECTED = 4;   // connected, waiting for close()
45     public static final int STATE_CLOSED = 5;   // was connected, now closed.
46 
47     private int mState;
48     private int mNativeData;
49     private Handler mHandler;
50     private int mAcceptedCode;
51     private int mConnectedCode;
52     private int mClosedCode;
53 
54     private WakeLock mWakeLock;  // held while in STATE_CONNECTING
55 
56     static {
classInitNative()57         classInitNative();
58     }
classInitNative()59     private native static void classInitNative();
60 
ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode, int closedCode)61     public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode,
62                      int closedCode) {
63         initNative();
64         mState = STATE_READY;
65         mHandler = handler;
66         mAcceptedCode = acceptedCode;
67         mConnectedCode = connectedCode;
68         mClosedCode = closedCode;
69         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket");
70         mWakeLock.setReferenceCounted(false);
71         if (VDBG) log(this + " SCO OBJECT CTOR");
72     }
initNative()73     private native void initNative();
74 
finalize()75     protected void finalize() throws Throwable {
76         try {
77             if (VDBG) log(this + " SCO OBJECT DTOR");
78             destroyNative();
79             releaseWakeLockNow();
80         } finally {
81             super.finalize();
82         }
83     }
destroyNative()84     private native void destroyNative();
85 
86     /** Connect this SCO socket to the given BT address.
87      *  Does not block.
88      */
connect(String address, String name)89     public synchronized boolean connect(String address, String name) {
90         if (DBG) log("connect() " + this);
91         if (mState != STATE_READY) {
92             if (DBG) log("connect(): Bad state");
93             return false;
94         }
95         acquireWakeLock();
96         if (connectNative(address, name)) {
97             mState = STATE_CONNECTING;
98             return true;
99         } else {
100             mState = STATE_CLOSED;
101             releaseWakeLockNow();
102             return false;
103         }
104     }
connectNative(String address, String name)105     private native boolean connectNative(String address, String name);
106 
107     /** Accept incoming SCO connections.
108      *  Does not block.
109      */
accept()110     public synchronized boolean accept() {
111         if (VDBG) log("accept() " + this);
112         if (mState != STATE_READY) {
113             if (DBG) log("Bad state");
114             return false;
115         }
116         if (acceptNative()) {
117             mState = STATE_ACCEPT;
118             return true;
119         } else {
120             mState = STATE_CLOSED;
121             return false;
122         }
123     }
acceptNative()124     private native boolean acceptNative();
125 
close()126     public synchronized void close() {
127         if (DBG) log(this + " SCO OBJECT close() mState = " + mState);
128         acquireWakeLock();
129         mState = STATE_CLOSED;
130         closeNative();
131         releaseWakeLock();
132     }
closeNative()133     private native void closeNative();
134 
getState()135     public synchronized int getState() {
136         return mState;
137     }
138 
onConnected(int result)139     private synchronized void onConnected(int result) {
140         if (VDBG) log(this + " onConnected() mState = " + mState + " " + this);
141         if (mState != STATE_CONNECTING) {
142             if (DBG) log("Strange state, closing " + mState + " " + this);
143             return;
144         }
145         if (result >= 0) {
146             mState = STATE_CONNECTED;
147         } else {
148             mState = STATE_CLOSED;
149         }
150         mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget();
151         releaseWakeLockNow();
152     }
153 
onAccepted(int result)154     private synchronized void onAccepted(int result) {
155         if (VDBG) log("onAccepted() " + this);
156         if (mState != STATE_ACCEPT) {
157             if (DBG) log("Strange state " + this);
158             return;
159         }
160         if (result >= 0) {
161             mState = STATE_CONNECTED;
162         } else {
163             mState = STATE_CLOSED;
164         }
165         mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget();
166     }
167 
onClosed()168     private synchronized void onClosed() {
169         if (DBG) log("onClosed() " + this);
170         if (mState != STATE_CLOSED) {
171             mState = STATE_CLOSED;
172             mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget();
173             releaseWakeLock();
174         }
175     }
176 
acquireWakeLock()177     private void acquireWakeLock() {
178         if (!mWakeLock.isHeld()) {
179             mWakeLock.acquire();
180             if (VDBG) log("mWakeLock.acquire() " + this);
181         }
182     }
183 
releaseWakeLock()184     private void releaseWakeLock() {
185         if (mWakeLock.isHeld()) {
186             // Keep apps processor awake for a further 2 seconds.
187             // This is a hack to resolve issue http://b/1616263 - in which
188             // we are left in a 80 mA power state when remotely terminating a
189             // call while connected to BT headset "HTC BH S100 " with A2DP and
190             // HFP profiles.
191             if (VDBG) log("mWakeLock.release() in 2 sec" + this);
192             mWakeLock.acquire(2000);
193         }
194     }
195 
releaseWakeLockNow()196     private void releaseWakeLockNow() {
197         if (mWakeLock.isHeld()) {
198             if (VDBG) log("mWakeLock.release() now" + this);
199             mWakeLock.release();
200         }
201     }
202 
log(String msg)203     private void log(String msg) {
204         Log.d(TAG, msg);
205     }
206 }
207