• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.PowerManager;
21 import android.os.PowerManager.WakeLock;
22 import android.util.Log;
23 
24 /**
25  * The Android Bluetooth API is not finalized, and *will* change. Use at your
26  * own risk.
27  *
28  * The base RFCOMM (service) connection for a headset or handsfree device.
29  *
30  * In the future this class will be removed.
31  *
32  * @hide
33  */
34 public final class HeadsetBase {
35     private static final String TAG = "Bluetooth HeadsetBase";
36     private static final boolean DBG = false;
37 
38     public static final int RFCOMM_DISCONNECTED = 1;
39 
40     public static final int DIRECTION_INCOMING = 1;
41     public static final int DIRECTION_OUTGOING = 2;
42 
43     private static int sAtInputCount = 0;  /* TODO: Consider not using a static variable */
44 
45     private final BluetoothAdapter mAdapter;
46     private final BluetoothDevice mRemoteDevice;
47     private final String mAddress;  // for native code
48     private final int mRfcommChannel;
49     private int mNativeData;
50     private Thread mEventThread;
51     private volatile boolean mEventThreadInterrupted;
52     private Handler mEventThreadHandler;
53     private int mTimeoutRemainingMs;
54     private final int mDirection;
55     private final long mConnectTimestamp;
56 
57     protected AtParser mAtParser;
58 
59     private WakeLock mWakeLock;  // held while processing an AT command
60 
classInitNative()61     private native static void classInitNative();
62     static {
classInitNative()63         classInitNative();
64     }
65 
finalize()66     protected void finalize() throws Throwable {
67         try {
68             cleanupNativeDataNative();
69             releaseWakeLock();
70         } finally {
71             super.finalize();
72         }
73     }
74 
cleanupNativeDataNative()75     private native void cleanupNativeDataNative();
76 
HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, int rfcommChannel)77     public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
78             int rfcommChannel) {
79         mDirection = DIRECTION_OUTGOING;
80         mConnectTimestamp = System.currentTimeMillis();
81         mAdapter = adapter;
82         mRemoteDevice = device;
83         mAddress = device.getAddress();
84         mRfcommChannel = rfcommChannel;
85         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
86         mWakeLock.setReferenceCounted(false);
87         initializeAtParser();
88         // Must be called after this.mAddress is set.
89         initializeNativeDataNative(-1);
90     }
91 
92     /* Create from an already existing rfcomm connection */
HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, int socketFd, int rfcommChannel, Handler handler)93     public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
94             int socketFd, int rfcommChannel, Handler handler) {
95         mDirection = DIRECTION_INCOMING;
96         mConnectTimestamp = System.currentTimeMillis();
97         mAdapter = adapter;
98         mRemoteDevice = device;
99         mAddress = device.getAddress();
100         mRfcommChannel = rfcommChannel;
101         mEventThreadHandler = handler;
102         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
103         mWakeLock.setReferenceCounted(false);
104         initializeAtParser();
105         // Must be called after this.mAddress is set.
106         initializeNativeDataNative(socketFd);
107     }
108 
initializeNativeDataNative(int socketFd)109     private native void initializeNativeDataNative(int socketFd);
110 
111     /* Process an incoming AT command line
112      */
handleInput(String input)113     protected void handleInput(String input) {
114         acquireWakeLock();
115         long timestamp;
116 
117         synchronized(HeadsetBase.class) {
118             if (sAtInputCount == Integer.MAX_VALUE) {
119                 sAtInputCount = 0;
120             } else {
121                 sAtInputCount++;
122             }
123         }
124 
125         if (DBG) timestamp = System.currentTimeMillis();
126         AtCommandResult result = mAtParser.process(input);
127         if (DBG) Log.d(TAG, "Processing " + input + " took " +
128                        (System.currentTimeMillis() - timestamp) + " ms");
129 
130         if (result.getResultCode() == AtCommandResult.ERROR) {
131             Log.i(TAG, "Error processing <" + input + ">");
132         }
133 
134         sendURC(result.toString());
135 
136         releaseWakeLock();
137     }
138 
139     /**
140      * Register AT commands that are common to all Headset / Handsets. This
141      * function is called by the HeadsetBase constructor.
142      */
initializeAtParser()143     protected void initializeAtParser() {
144         mAtParser = new AtParser();
145         //TODO(): Get rid of this as there are no parsers registered. But because of dependencies,
146         //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree
147     }
148 
getAtParser()149     public AtParser getAtParser() {
150         return mAtParser;
151     }
152 
startEventThread()153     public void startEventThread() {
154         mEventThread =
155             new Thread("HeadsetBase Event Thread") {
156                 public void run() {
157                     int last_read_error;
158                     while (!mEventThreadInterrupted) {
159                         String input = readNative(500);
160                         if (input != null) {
161                             handleInput(input);
162                         }
163                         else {
164                             last_read_error = getLastReadStatusNative();
165                             if (last_read_error != 0) {
166                                 Log.i(TAG, "headset read error " + last_read_error);
167                                 if (mEventThreadHandler != null) {
168                                     mEventThreadHandler.obtainMessage(RFCOMM_DISCONNECTED)
169                                             .sendToTarget();
170                                 }
171                                 disconnectNative();
172                                 break;
173                             }
174                         }
175                     }
176                 }
177             };
178         mEventThreadInterrupted = false;
179         mEventThread.start();
180     }
181 
182 
183 
readNative(int timeout_ms)184     private native String readNative(int timeout_ms);
getLastReadStatusNative()185     private native int getLastReadStatusNative();
186 
stopEventThread()187     private void stopEventThread() {
188         mEventThreadInterrupted = true;
189         mEventThread.interrupt();
190         try {
191             mEventThread.join();
192         } catch (java.lang.InterruptedException e) {
193             // FIXME: handle this,
194         }
195         mEventThread = null;
196     }
197 
connect(Handler handler)198     public boolean connect(Handler handler) {
199         if (mEventThread == null) {
200             if (!connectNative()) return false;
201             mEventThreadHandler = handler;
202         }
203         return true;
204     }
connectNative()205     private native boolean connectNative();
206 
207     /*
208      * Returns true when either the asynchronous connect is in progress, or
209      * the connect is complete.  Call waitForAsyncConnect() to find out whether
210      * the connect is actually complete, or disconnect() to cancel.
211      */
212 
connectAsync()213     public boolean connectAsync() {
214         int ret = connectAsyncNative();
215         return (ret == 0) ? true : false;
216     }
connectAsyncNative()217     private native int connectAsyncNative();
218 
getRemainingAsyncConnectWaitingTimeMs()219     public int getRemainingAsyncConnectWaitingTimeMs() {
220         return mTimeoutRemainingMs;
221     }
222 
223     /*
224      * Returns 1 when an async connect is complete, 0 on timeout, and -1 on
225      * error.  On error, handler will be called, and you need to re-initiate
226      * the async connect.
227      */
waitForAsyncConnect(int timeout_ms, Handler handler)228     public int waitForAsyncConnect(int timeout_ms, Handler handler) {
229         int res = waitForAsyncConnectNative(timeout_ms);
230         if (res > 0) {
231             mEventThreadHandler = handler;
232         }
233         return res;
234     }
waitForAsyncConnectNative(int timeout_ms)235     private native int waitForAsyncConnectNative(int timeout_ms);
236 
disconnect()237     public void disconnect() {
238         if (mEventThread != null) {
239             stopEventThread();
240         }
241         disconnectNative();
242     }
disconnectNative()243     private native void disconnectNative();
244 
245 
246     /*
247      * Note that if a remote side disconnects, this method will still return
248      * true until disconnect() is called.  You know when a remote side
249      * disconnects because you will receive the intent
250      * IBluetoothService.REMOTE_DEVICE_DISCONNECTED_ACTION.  If, when you get
251      * this intent, method isConnected() returns true, you know that the
252      * disconnect was initiated by the remote device.
253      */
254 
isConnected()255     public boolean isConnected() {
256         return mEventThread != null;
257     }
258 
getRemoteDevice()259     public BluetoothDevice getRemoteDevice() {
260         return mRemoteDevice;
261     }
262 
getDirection()263     public int getDirection() {
264         return mDirection;
265     }
266 
getConnectTimestamp()267     public long getConnectTimestamp() {
268         return mConnectTimestamp;
269     }
270 
sendURC(String urc)271     public synchronized boolean sendURC(String urc) {
272         if (urc.length() > 0) {
273             boolean ret = sendURCNative(urc);
274             return ret;
275         }
276         return true;
277     }
sendURCNative(String urc)278     private native boolean sendURCNative(String urc);
279 
acquireWakeLock()280     private synchronized void acquireWakeLock() {
281         if (!mWakeLock.isHeld()) {
282             mWakeLock.acquire();
283         }
284     }
285 
releaseWakeLock()286     private synchronized void releaseWakeLock() {
287         if (mWakeLock.isHeld()) {
288             mWakeLock.release();
289         }
290     }
291 
getAtInputCount()292     public static int getAtInputCount() {
293         return sAtInputCount;
294     }
295 
log(String msg)296     private static void log(String msg) {
297         Log.d(TAG, msg);
298     }
299 }
300