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

Commit 45399949 authored by Sharvil Nanavati's avatar Sharvil Nanavati
Browse files

Expose wake lock / wake alarm functionality to bluedroid.

Change-Id: I267ff1d19a0d1ef5ad9a290c7654d7e6ee3a9f15
parent b7d96bb1
Loading
Loading
Loading
Loading
+133 −3
Original line number Diff line number Diff line
@@ -41,11 +41,15 @@ static jmethodID method_sspRequestCallback;
static jmethodID method_bondStateChangeCallback;
static jmethodID method_aclStateChangeCallback;
static jmethodID method_discoveryStateChangeCallback;
static jmethodID method_setWakeAlarm;
static jmethodID method_acquireWakeLock;
static jmethodID method_releaseWakeLock;

static const bt_interface_t *sBluetoothInterface = NULL;
static const btsock_interface_t *sBluetoothSocketInterface = NULL;
static JNIEnv *callbackEnv = NULL;

static jobject sJniAdapterServiceObj;
static jobject sJniCallbacksObj;
static jfieldID sJniCallbacksField;

@@ -439,7 +443,7 @@ static void le_test_mode_recv_callback (bt_status_t status, uint16_t packet_coun

    ALOGV("%s: status:%d packet_count:%d ", __FUNCTION__, status, packet_count);
}
bt_callbacks_t sBluetoothCallbacks = {
static bt_callbacks_t sBluetoothCallbacks = {
    sizeof(sBluetoothCallbacks),
    adapter_state_change_callback,
    adapter_properties_callback,
@@ -456,6 +460,116 @@ bt_callbacks_t sBluetoothCallbacks = {
    le_test_mode_recv_callback
};

// The callback to call when the wake alarm fires.
static alarm_cb sAlarmCallback;

// The data to pass to the wake alarm callback.
static void *sAlarmCallbackData;

static bool set_wake_alarm_callout(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data) {
    JNIEnv *env;
    JavaVM *vm = AndroidRuntime::getJavaVM();
    jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6);

    if (status != JNI_OK && status != JNI_EDETACHED) {
        ALOGE("%s unable to get environment for JNI call", __func__);
        return false;
    }

    if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, NULL) != 0) {
        ALOGE("%s unable to attach thread to VM", __func__);
        return false;
    }

    jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE;
    jboolean ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm, (jlong)delay_millis, jshould_wake);
    if (ret) {
        sAlarmCallback = cb;
        sAlarmCallbackData = data;
    }

    if (status == JNI_EDETACHED) {
        vm->DetachCurrentThread();
    }

    return !!ret;
}

static int acquire_wake_lock_callout(const char *lock_name) {
    JNIEnv *env;
    JavaVM *vm = AndroidRuntime::getJavaVM();
    jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6);

    if (status != JNI_OK && status != JNI_EDETACHED) {
        ALOGE("%s unable to get environment for JNI call", __func__);
        return BT_STATUS_FAIL;
    }

    if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, NULL) != 0) {
        ALOGE("%s unable to attach thread to VM", __func__);
        return BT_STATUS_FAIL;
    }

    jboolean ret = JNI_FALSE;
    jstring lock_name_jni = env->NewStringUTF(lock_name);
    if (lock_name_jni) {
        ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_acquireWakeLock, lock_name_jni);
        env->DeleteLocalRef(lock_name_jni);
    } else {
        ALOGE("%s unable to allocate string: %s", __func__, lock_name);
    }

    if (status == JNI_EDETACHED) {
        vm->DetachCurrentThread();
    }

    return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
}

static int release_wake_lock_callout(const char *lock_name) {
    JNIEnv *env;
    JavaVM *vm = AndroidRuntime::getJavaVM();
    jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6);

    if (status != JNI_OK && status != JNI_EDETACHED) {
        ALOGE("%s unable to get environment for JNI call", __func__);
        return BT_STATUS_FAIL;
    }

    if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, NULL) != 0) {
        ALOGE("%s unable to attach thread to VM", __func__);
        return BT_STATUS_FAIL;
    }

    jboolean ret = JNI_FALSE;
    jstring lock_name_jni = env->NewStringUTF(lock_name);
    if (lock_name_jni) {
        ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_releaseWakeLock, lock_name_jni);
        env->DeleteLocalRef(lock_name_jni);
    } else {
        ALOGE("%s unable to allocate string: %s", __func__, lock_name);
    }

    if (status == JNI_EDETACHED) {
        vm->DetachCurrentThread();
    }

    return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
}

// Called by Java code when alarm is fired. A wake lock is held by the caller
// over the duration of this callback.
static void alarmFiredNative(JNIEnv *env, jobject obj) {
    sAlarmCallback(sAlarmCallbackData);
}

static bt_os_callouts_t sBluetoothOsCallouts = {
    sizeof(sBluetoothOsCallouts),
    set_wake_alarm_callout,
    acquire_wake_lock_callout,
    release_wake_lock_callout,
};

static void classInitNative(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;
@@ -487,6 +601,11 @@ static void classInitNative(JNIEnv* env, jclass clazz) {

    method_aclStateChangeCallback = env->GetMethodID(jniCallbackClass,
                                                    "aclStateChangeCallback", "(I[BI)V");

    method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
    method_acquireWakeLock = env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
    method_releaseWakeLock = env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");

    char value[PROPERTY_VALUE_MAX];
    property_get("bluetooth.mock_stack", value, "");

@@ -511,15 +630,24 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
static bool initNative(JNIEnv* env, jobject obj) {
    ALOGV("%s:",__FUNCTION__);

    sJniAdapterServiceObj = env->NewGlobalRef(obj);
    sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));

    if (sBluetoothInterface) {
        int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
        if (ret != BT_STATUS_SUCCESS) {
            ALOGE("Error while setting the callbacks \n");
            ALOGE("Error while setting the callbacks: %d\n", ret);
            sBluetoothInterface = NULL;
            return JNI_FALSE;
        }
        ret = sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts);
        if (ret != BT_STATUS_SUCCESS) {
            ALOGE("Error while setting Bluetooth callouts: %d\n", ret);
            sBluetoothInterface->cleanup();
            sBluetoothInterface = NULL;
            return JNI_FALSE;
        }

        if ( (sBluetoothSocketInterface = (btsock_interface_t *)
                  sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
                ALOGE("Error getting socket interface");
@@ -539,6 +667,7 @@ static bool cleanupNative(JNIEnv *env, jobject obj) {
    ALOGI("%s: return from cleanup",__FUNCTION__);

    env->DeleteGlobalRef(sJniCallbacksObj);
    env->DeleteGlobalRef(sJniAdapterServiceObj);
    return JNI_TRUE;
}

@@ -942,7 +1071,8 @@ static JNINativeMethod sMethods[] = {
    {"connectSocketNative", "([BI[BII)I", (void*) connectSocketNative},
    {"createSocketChannelNative", "(ILjava/lang/String;[BII)I",
     (void*) createSocketChannelNative},
    {"configHciSnoopLogNative", "(Z)Z", (void*) configHciSnoopLogNative}
    {"configHciSnoopLogNative", "(Z)Z", (void*) configHciSnoopLogNative},
    {"alarmFiredNative", "()V", (void *) alarmFiredNative},
};

int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
+100 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@

package com.android.bluetooth.btservice;

import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -42,9 +44,11 @@ import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
@@ -83,6 +87,9 @@ public class AdapterService extends Service {
    public static final int PROFILE_CONN_CONNECTED  = 1;
    public static final int PROFILE_CONN_REJECTED  = 2;

    private static final String ACTION_ALARM_WAKEUP =
        "com.android.bluetooth.btservice.action.ALARM_WAKEUP";

    static final String BLUETOOTH_ADMIN_PERM =
        android.Manifest.permission.BLUETOOTH_ADMIN;
    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -141,6 +148,12 @@ public class AdapterService extends Service {
    private int mCurrentRequestId;
    private boolean mQuietmode = false;

    private AlarmManager mAlarmManager;
    private PendingIntent mPendingAlarm;
    private PowerManager mPowerManager;
    private PowerManager.WakeLock mWakeLock;
    private String mWakeLockName;

    public AdapterService() {
        super();
        if (TRACE_REF) {
@@ -303,7 +316,10 @@ public class AdapterService extends Service {
        //Load the name and address
        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR);
        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME);
        mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);

        registerReceiver(mAlarmBroadcastReceiver, new IntentFilter(ACTION_ALARM_WAKEUP));
    }

    @Override
@@ -388,6 +404,18 @@ public class AdapterService extends Service {

        mCleaningUp = true;

        unregisterReceiver(mAlarmBroadcastReceiver);

        if (mPendingAlarm != null) {
            mAlarmManager.cancel(mPendingAlarm);
            mPendingAlarm = null;
        }

        if (mWakeLock != null) {
            mWakeLock.release();
            mWakeLock = null;
        }

        if (mAdapterStateMachine != null) {
            mAdapterStateMachine.doQuit();
            mAdapterStateMachine.cleanup();
@@ -1461,6 +1489,66 @@ public class AdapterService extends Service {
        return -1;
    }

    // This function is called from JNI. It allows native code to set a single wake
    // alarm. If an alarm is already pending and a new request comes in, the alarm
    // will be rescheduled (i.e. the previously set alarm will be cancelled).
    private boolean setWakeAlarm(long delayMillis, boolean shouldWake) {
        synchronized (this) {
            if (mPendingAlarm != null) {
                mAlarmManager.cancel(mPendingAlarm);
            }

            long wakeupTime = SystemClock.elapsedRealtime() + delayMillis;
            int type = shouldWake
                ? AlarmManager.ELAPSED_REALTIME_WAKEUP
                : AlarmManager.ELAPSED_REALTIME;

            Intent intent = new Intent(ACTION_ALARM_WAKEUP);
            mPendingAlarm = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
            mAlarmManager.setExact(type, wakeupTime, mPendingAlarm);
            return true;
        }
    }

    // This function is called from JNI. It allows native code to acquire a single wake lock.
    // If the wake lock is already held, this function returns success. Although this function
    // only supports acquiring a single wake lock at a time right now, it will eventually be
    // extended to allow acquiring an arbitrary number of wake locks. The current interface
    // takes |lockName| as a parameter in anticipation of that implementation.
    private boolean acquireWakeLock(String lockName) {
        if (mWakeLock != null) {
            if (!lockName.equals(mWakeLockName)) {
                errorLog("Multiple wake lock acquisition attempted; aborting: " + lockName);
                return false;
            }

            // We're already holding the desired wake lock so return success.
            if (mWakeLock.isHeld()) {
                return true;
            }
        }

        mWakeLockName = lockName;
        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
        mWakeLock.acquire();
        return true;
    }

    // This function is called from JNI. It allows native code to release a wake lock acquired
    // by |acquireWakeLock|. If the wake lock is not held, this function returns failure. See
    // the comment for |acquireWakeLock| for an explanation of the interface.
    private boolean releaseWakeLock(String lockName) {
        if (mWakeLock == null) {
            errorLog("Repeated wake lock release; aborting release: " + lockName);
            return false;
        }

        mWakeLock.release();
        mWakeLock = null;
        mWakeLockName = null;
        return true;
    }

    private void debugLog(String msg) {
        Log.d(TAG +"(" +hashCode()+")", msg);
    }
@@ -1469,6 +1557,16 @@ public class AdapterService extends Service {
        Log.e(TAG +"(" +hashCode()+")", msg);
    }

    private final BroadcastReceiver mAlarmBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (AdapterService.this) {
                mPendingAlarm = null;
                alarmFiredNative();
            }
        }
    };

    private native static void classInitNative();
    private native boolean initNative();
    private native void cleanupNative();
@@ -1503,6 +1601,8 @@ public class AdapterService extends Service {

    /*package*/ native boolean configHciSnoopLogNative(boolean enable);

    private native void alarmFiredNative();

    protected void finalize() {
        cleanup();
        if (TRACE_REF) {