Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a39a4f83 authored by Nitin Arora's avatar Nitin Arora Committed by Andre Eisenbach
Browse files

Bluetooth LE background operation mode (1/2)

Changes to BluetoothAdapter state machine for handling new
states BLE_STATE_ON and intermediate states BLE_TURNING_ON
and BLE_TURNING_OFF. Also includes the actions required
while migrating in and out of these states including
start and stop of GATT services and other profiles.

Change-Id: I08d6ff05c2cefe559d5bb5789a1a488afefbe544
parent 53e38875
Loading
Loading
Loading
Loading
+35 −12
Original line number Diff line number Diff line
@@ -59,13 +59,18 @@ static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, btrc_remote_f
    }
    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr for connection state");
        ALOGE("Unable to allocate byte array for bd_addr");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    if (mCallbacksObj) {
        sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    /* TODO: I think we leak the addr object, we should add a
     * sCallbackEnv->DeleteLocalRef(addr) */
@@ -79,7 +84,11 @@ static void btavrcp_get_play_status_callback() {
        return;
    }

    if (mCallbacksObj) {
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

@@ -99,7 +108,11 @@ static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_
        return;
    }
    sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
    if (mCallbacksObj) {
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(attrs);
}
@@ -111,9 +124,12 @@ static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uin
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
        return;
    }

    if (mCallbacksObj) {
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
                                 (jint)event_id, (jint)param);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

@@ -124,9 +140,13 @@ static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) {
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
        return;
    }

    if (mCallbacksObj) {
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume,
                                                     (jint)ctype);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

@@ -137,9 +157,12 @@ static void btavrcp_passthrough_command_callback(int id, int pressed) {
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
        return;
    }

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id,
                                                                             (jint)pressed);
    if (mCallbacksObj) {
        sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
                      (jint)id, (jint)pressed);
    } else {
        ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
    }
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

+0 −0

File mode changed from 100755 to 100644.

+7 −2
Original line number Diff line number Diff line
@@ -134,14 +134,19 @@ static void initializeNative(JNIEnv *env, jobject object) {
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);

    bt_status_t status;
    if ( (status = sPanIf->init(&sBluetoothPanCallbacks)) != BT_STATUS_SUCCESS) {
        error("Failed to initialize Bluetooth PAN, status: %d", status);
        sPanIf = NULL;
        if (mCallbacksObj != NULL) {
            ALOGW("initialization failed: Cleaning up Bluetooth PAN callback object");
            env->DeleteGlobalRef(mCallbacksObj);
            mCallbacksObj = NULL;
        }
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);
}

static void cleanupNative(JNIEnv *env, jobject object) {
+13 −1
Original line number Diff line number Diff line
@@ -606,7 +606,18 @@ class AdapterProperties {

    private boolean mBluetoothDisabling = false;

    void onBleDisable() {
        // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state.
        // When BT disable is invoked, set the scan_mode to NONE
        // so no incoming connections are possible
        debugLog("onBleDisable");
        if (getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
           setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
        }
    }

    void onBluetoothDisable() {
        // From STATE_ON to BLE_ON
        // When BT disable is invoked, set the scan_mode to NONE
        // so no incoming connections are possible

@@ -618,6 +629,7 @@ class AdapterProperties {
            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
        }
    }

    void discoveryStateChangeCallback(int state) {
        infoLog("Callback:discoveryStateChangeCallback with state:" + state);
        synchronized (mObject) {
+149 −34
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@ import android.os.ServiceManager;

public class AdapterService extends Service {
    private static final String TAG = "BluetoothAdapterService";
    private static final boolean DBG = false;
    private static final boolean DBG = true;
    private static final boolean TRACE_REF = true;
    private static final int MIN_ADVT_INSTANCES_FOR_MA = 5;
    private static final int MIN_OFFLOADED_FILTERS = 10;
@@ -287,6 +287,8 @@ public class AdapterService extends Service {

    private void processProfileServiceStateChanged(String serviceName, int state) {
        boolean doUpdate=false;
        boolean isBleTurningOn;
        boolean isBleTurningOff;
        boolean isTurningOn;
        boolean isTurningOff;

@@ -307,15 +309,41 @@ public class AdapterService extends Service {
        synchronized (mAdapterStateMachine) {
            isTurningOff = mAdapterStateMachine.isTurningOff();
            isTurningOn = mAdapterStateMachine.isTurningOn();
            isBleTurningOn = mAdapterStateMachine.isBleTurningOn();
            isBleTurningOff = mAdapterStateMachine.isBleTurningOff();
        }

        if (isTurningOff) {
        debugLog("processProfileServiceStateChanged() - serviceName=" + serviceName +
                 " isTurningOn=" + isTurningOn + " isTurningOff=" + isTurningOff +
                 " isBleTurningOn=" + isBleTurningOn + " isBleTurningOff=" + isBleTurningOff);

        if (isBleTurningOn) {
            if (serviceName.equals("com.android.bluetooth.gatt.GattService")) {
                debugLog("GattService is started");
                mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BLE_STARTED));
                return;
            }

        } else if(isBleTurningOff) {
            if (serviceName.equals("com.android.bluetooth.gatt.GattService")) {
                debugLog("GattService stopped");
                mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BLE_STOPPED));
                return;
            }

        } else if (isTurningOff) {
            //On to BLE_ON
            //Process stop or disable pending
            //Check if all services are stopped if so, do cleanup
            synchronized (mProfileServicesState) {
                Iterator<Map.Entry<String,Integer>> i = mProfileServicesState.entrySet().iterator();
                while (i.hasNext()) {
                    Map.Entry<String,Integer> entry = i.next();
                    debugLog("Service: " + entry.getKey());
                    if (entry.getKey().equals("com.android.bluetooth.gatt.GattService")) {
                        debugLog("Skip GATT service - already started before");
                        continue;
                    }
                    if (BluetoothAdapter.STATE_OFF != entry.getValue()) {
                        debugLog("onProfileServiceStateChange() - Profile still running: "
                            + entry.getKey());
@@ -326,7 +354,8 @@ public class AdapterService extends Service {
            debugLog("onProfileServiceStateChange() - All profile services stopped...");
            //Send message to state machine
            mProfilesStarted=false;
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STOPPED));
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STOPPED));

        } else if (isTurningOn) {
            //Process start pending
            //Check if all services are started if so, update state
@@ -334,6 +363,11 @@ public class AdapterService extends Service {
                Iterator<Map.Entry<String,Integer>> i = mProfileServicesState.entrySet().iterator();
                while (i.hasNext()) {
                    Map.Entry<String,Integer> entry = i.next();
                    debugLog("Service: " + entry.getKey());
                    if (entry.getKey().equals("com.android.bluetooth.gatt.GattService")) {
                        debugLog("Skip GATT service - already started before");
                        continue;
                    }
                    if (BluetoothAdapter.STATE_ON != entry.getValue()) {
                        debugLog("onProfileServiceStateChange() - Profile still not running:"
                            + entry.getKey());
@@ -344,7 +378,7 @@ public class AdapterService extends Service {
            debugLog("onProfileServiceStateChange() - All profile services started.");
            mProfilesStarted=true;
            //Send message to state machine
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED));
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STARTED));
        }
    }

@@ -387,8 +421,8 @@ public class AdapterService extends Service {
        debugLog("onDestroy()");
    }

    void processStart() {
        debugLog("processStart()");
    void BleOnProcessStart() {
        debugLog("BleOnProcessStart()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        //Initialize data objects
        for (int i=0; i < supportedProfileServices.length;i++) {
@@ -397,7 +431,7 @@ public class AdapterService extends Service {
        mRemoteDevices = new RemoteDevices(this);
        mAdapterProperties.init(mRemoteDevices);

        debugLog("processStart() - Make Bond State Machine");
        debugLog("BleOnProcessStart() - Make Bond State Machine");
        mBondStateMachine = BondStateMachine.make(this, mAdapterProperties, mRemoteDevices);

        mJniCallbacks.init(mBondStateMachine,mRemoteDevices);
@@ -405,13 +439,22 @@ public class AdapterService extends Service {
        //FIXME: Set static instance here???
        setAdapterService(this);

        //Start Gatt service
        setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
    }

    void startCoreServices()
    {
        debugLog("startCoreServices()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();

        //Start profile services
        if (!mProfilesStarted && supportedProfileServices.length >0) {
            //Startup all profile services
            setProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
        }else {
            debugLog("processStart() - Profile Services alreay started");
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED));
            debugLog("startCoreProfiles(): Profile Services alreay started");
            mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STARTED));
        }
    }

@@ -429,6 +472,16 @@ public class AdapterService extends Service {
        return false;
    }

    boolean stopGattProfileService() {
        //TODO: can optimize this instead of looping around all supported profiles
        debugLog("stopGattProfileService()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();

        setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_OFF);
        return true;
    }


     void updateAdapterState(int prevState, int newState){
        if (mCallbacks !=null) {
            int n=mCallbacks.beginBroadcast();
@@ -517,7 +570,6 @@ public class AdapterService extends Service {
            mCallbacks.kill();
        }

        debugLog("cleanup() - Bluetooth process exited normally.");
        System.exit(0);
    }

@@ -575,6 +627,51 @@ public class AdapterService extends Service {
        }
    };

    @SuppressWarnings("rawtypes")
    private void setGattProfileServiceState(Class[] services, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            Log.w(TAG,"setGattProfileServiceState(): invalid state...Leaving...");
            return;
        }

        int expectedCurrentState= BluetoothAdapter.STATE_OFF;
        int pendingState = BluetoothAdapter.STATE_TURNING_ON;

        if (state == BluetoothAdapter.STATE_OFF) {
            expectedCurrentState= BluetoothAdapter.STATE_ON;
            pendingState = BluetoothAdapter.STATE_TURNING_OFF;
        }

        for (int i=0; i <services.length;i++) {
            String serviceName = services[i].getName();
            String simpleName = services[i].getSimpleName();

            if (simpleName.equals("GattService")) {
                if (DBG) Log.d(TAG, "It is gaTT service");
                Integer serviceState = mProfileServicesState.get(serviceName);

                if(serviceState != null && serviceState != expectedCurrentState) {
                    debugLog("setProfileServiceState() - Unable to "
                        + (state == BluetoothAdapter.STATE_OFF ? "start" : "stop" )
                        + " service " + serviceName
                        + ". Invalid state: " + serviceState);
                        continue;
                }
                debugLog("setProfileServiceState() - "
                    + (state == BluetoothAdapter.STATE_OFF ? "Stopping" : "Starting")
                    + " service " + serviceName);

                mProfileServicesState.put(serviceName,pendingState);
                Intent intent = new Intent(this,services[i]);
                intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
                intent.putExtra(BluetoothAdapter.EXTRA_STATE,state);
                startService(intent);
                return;
            }
        }
    }


    @SuppressWarnings("rawtypes")
    private void setProfileServiceState(Class[] services, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
@@ -591,6 +688,12 @@ public class AdapterService extends Service {

        for (int i=0; i <services.length;i++) {
            String serviceName = services[i].getName();
            String simpleName = services[i].getSimpleName();

            if (simpleName.equals("GattService")) {
                if (DBG) Log.d(TAG, "It is gaTT service. SKIP IT");
                continue;
            }
            Integer serviceState = mProfileServicesState.get(serviceName);
            if(serviceState != null && serviceState != expectedCurrentState) {
                debugLog("setProfileServiceState() - Unable to "
@@ -1171,27 +1274,32 @@ public class AdapterService extends Service {
            }
            return service.dump();
         }
    };

         public void onLeServiceUp(){
             AdapterService service = getService();
             if (service == null) return;
             service.onLeServiceUp();
         }

         public void onBrEdrDown(){
             AdapterService service = getService();
             if (service == null) return;
             service.onBrEdrDown();
         }
    };

    // ----API Methods--------

     boolean isEnabled() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

        return mAdapterProperties.getState() == BluetoothAdapter.STATE_ON;
     }

     int getState() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

        if (mAdapterProperties == null){
        if (mAdapterProperties != null) return mAdapterProperties.getState();
        return  BluetoothAdapter.STATE_OFF;
     }
        else {
            debugLog("getState() - mAdapterProperties: " + mAdapterProperties);
            return mAdapterProperties.getState();
        }
    }

     boolean enable() {
        return enable (false);
@@ -1202,23 +1310,20 @@ public class AdapterService extends Service {
     }

     public synchronized boolean enable(boolean quietMode) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                        "Need BLUETOOTH ADMIN permission");
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");

         debugLog("enable() - Enable called with quiet mode status =  " + mQuietmode);
         mQuietmode = quietMode;
         Message m =
                 mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
         Message m = mAdapterStateMachine.obtainMessage(AdapterState.BLE_TURN_ON);
         mAdapterStateMachine.sendMessage(m);
         return true;
     }

     boolean disable() {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                       "Need BLUETOOTH ADMIN permission");
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");

        debugLog("disable() called...");
        Message m =
                mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
        Message m = mAdapterStateMachine.obtainMessage(AdapterState.BLE_TURN_OFF);
        mAdapterStateMachine.sendMessage(m);
        return true;
    }
@@ -1768,6 +1873,16 @@ public class AdapterService extends Service {
        return sb.toString();
    }

    public void onLeServiceUp() {
        Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
        mAdapterStateMachine.sendMessage(m);
    }

    public void onBrEdrDown() {
        Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
        mAdapterStateMachine.sendMessage(m);
    }

    private static int convertScanModeToHal(int mode) {
        switch (mode) {
            case BluetoothAdapter.SCAN_MODE_NONE:
@@ -1907,11 +2022,11 @@ public class AdapterService extends Service {
    }

    private void debugLog(String msg) {
        if (DBG) Log.d(TAG +"(" +hashCode()+")", msg);
        if (DBG) Log.d(TAG, msg);
    }

    private void errorLog(String msg) {
        Log.e(TAG +"(" +hashCode()+")", msg);
        Log.e(TAG, msg);
    }

    private final BroadcastReceiver mAlarmBroadcastReceiver = new BroadcastReceiver() {
Loading