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