Home | History | Annotate | Download | only in bluetooth
      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 {
     57         classInitNative();
     58     }
     59     private native static void classInitNative();
     60 
     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     }
     73     private native void initNative();
     74 
     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     }
     84     private native void destroyNative();
     85 
     86     /** Connect this SCO socket to the given BT address.
     87      *  Does not block.
     88      */
     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     }
    105     private native boolean connectNative(String address, String name);
    106 
    107     /** Accept incoming SCO connections.
    108      *  Does not block.
    109      */
    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     }
    124     private native boolean acceptNative();
    125 
    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     }
    133     private native void closeNative();
    134 
    135     public synchronized int getState() {
    136         return mState;
    137     }
    138 
    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 
    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 
    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 
    177     private void acquireWakeLock() {
    178         if (!mWakeLock.isHeld()) {
    179             mWakeLock.acquire();
    180             if (VDBG) log("mWakeLock.acquire() " + this);
    181         }
    182     }
    183 
    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 
    196     private void releaseWakeLockNow() {
    197         if (mWakeLock.isHeld()) {
    198             if (VDBG) log("mWakeLock.release() now" + this);
    199             mWakeLock.release();
    200         }
    201     }
    202 
    203     private void log(String msg) {
    204         Log.d(TAG, msg);
    205     }
    206 }
    207