android5.0(Lollipop) BLE Peripheral深入理解系统篇之提高篇

上一篇文章讲到了广播之前系统需要进行的准备工作,那接下来我们就来真正的启动广播。

首先还是先看一下上一篇文章结束的地方:

@Override
public void onClientRegistered(int status, int clientIf) {
    Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
    synchronized (this) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mClientIf = clientIf;
            try {
                mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement,
                        mScanResponse, mSettings);
                return;
            } catch (RemoteException e) {
                Log.e(TAG, "failed to start advertising", e);
            }
        }
        // Registration failed.
        mClientIf = -1;
        notifyAll();
    }
}
现在让我们继续追踪mBluetoothGatt.startMultiAdvertising,但是我们发现mBluetoothGatt是通过AIDL來定义的:

private final IBluetoothGatt mBluetoothGatt;
到这里,我们就不去看IBluetoothGatt的内容了,因为都是一些接口函数,我们比较关心的是IBluetoothGatt是谁的接口呢,在android如果遇到这种情况,我们肯定要去找service了,那我们现在基本可以确定,我们找的是GattService了,那我们去验证一下,到底是不是这样的:

private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder
看到这里我们应该可以确认tartMultiAdvertising的实现是在这里,那我们找一下它的实现:

@Override
public void startMultiAdvertising(int clientIf, AdvertiseData advertiseData,
        AdvertiseData scanResponse, AdvertiseSettings settings) {
    GattService service = getService();
    if (service == null) return;
    service.startMultiAdvertising(clientIf, advertiseData, scanResponse, settings);
}
终于找到它的实现了,它却有用了GattService自己内部的实现,那我们继续看一下:

void startMultiAdvertising(int clientIf, AdvertiseData advertiseData,
        AdvertiseData scanResponse, AdvertiseSettings settings) {
    enforceAdminPermission();
    mAdvertiseManager.startAdvertising(new AdvertiseClient(clientIf, settings, advertiseData,
            scanResponse));
}
到这里,我们有可以比较顺利的追踪了,那我们继续看一下AdvertiserManager是怎样定义它的:

void startAdvertising(AdvertiseClient client) {
    if (client == null) {
        return;
    }
    Message message = new Message();
    message.what = MSG_START_ADVERTISING;
    message.obj = client;
    mHandler.sendMessage(message);
}
到这里,发现竟然只是发了一个handler message,那我们就去看看这个handler是怎么处理
@Override
public void handleMessage(Message msg) {
    logd("message : " + msg.what);
    AdvertiseClient client = (AdvertiseClient) msg.obj;
    switch (msg.what) {
        case MSG_START_ADVERTISING:
            handleStartAdvertising(client);
            break;
        case MSG_STOP_ADVERTISING:
            handleStopAdvertising(client);
            break;
        default:
            // Shouldn‘t happen.
            Log.e(TAG, "recieve an unknown message : " + msg.what);
            break;
    }
}
private void handleStartAdvertising(AdvertiseClient client) {
    Utils.enforceAdminPermission(mService);
    int clientIf = client.clientIf;
    if (mAdvertiseClients.contains(clientIf)) {
        postCallback(clientIf, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
        return;
    }  

    if (mAdvertiseClients.size() >= maxAdvertiseInstances()) {
        postCallback(clientIf,
                AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS);
        return;
    }
    if (!mAdvertiseNative.startAdverising(client)) {
        postCallback(clientIf, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
        return;
    }
    mAdvertiseClients.add(client);
    postCallback(clientIf, AdvertiseCallback.ADVERTISE_SUCCESS);
}
到这里了,我们也看到了关键函数mAdvertiseNative.startAdvertising函数:

boolean startAdverising(AdvertiseClient client) {
    if (!mAdapterService.isMultiAdvertisementSupported() &&
            !mAdapterService.isPeripheralModeSupported()) {
        return false;
    }
    if (mAdapterService.isMultiAdvertisementSupported()) {
        return startMultiAdvertising(client);
    }
    return startSingleAdvertising(client);
}
这里我们就选择任意一个函数就讲解,就选择startSingleAdvertising来讲好了:

boolean startSingleAdvertising(AdvertiseClient client) {
    logd("starting single advertising");
    resetCountDownLatch();
    enableAdvertising(client);
    if (!waitForCallback()) {
        return false;
    }
    setAdvertisingData(client, client.advertiseData, false);
    return true;
}
这里面有两个关键点,一个是使能Advertising,另一个是设置AdvertisingData要发的数据。我们先来看一下enableAdvertising:

private void enableAdvertising(AdvertiseClient client) {
    int clientIf = client.clientIf;
    int minAdvertiseUnit = (int) getAdvertisingIntervalUnit(client.settings);
    int maxAdvertiseUnit = minAdvertiseUnit + ADVERTISING_INTERVAL_DELTA_UNIT;
    int advertiseEventType = getAdvertisingEventType(client);
    int txPowerLevel = getTxPowerLevel(client.settings);
    int advertiseTimeoutSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(
            client.settings.getTimeout());
    if (mAdapterService.isMultiAdvertisementSupported()) {
        Log.i(TAG, "gattClientEnableAdvNative(" + clientIf + ",...);");
        gattClientEnableAdvNative(
                clientIf,
                minAdvertiseUnit, maxAdvertiseUnit,
                advertiseEventType,
                ADVERTISING_CHANNEL_ALL,
                txPowerLevel,
                advertiseTimeoutSeconds);
    } else {
        Log.i(TAG, "gattAdvertiseNative(" + client.clientIf + ",true);");
        gattAdvertiseNative(client.clientIf, true);
    }
}
这里面主要是在设置一些参数,比如最大广播间隔设置的是10ms+设置的最小间隔,TxPower的级别等等。这里面对于多广播和单广播调用的底层是不一样的。我们这里看看单广播gattAdvertiseNative(client.clientIf, true);实现如下:

static void gattAdvertiseNative(JNIEnv *env, jobject object,
        jint client_if, jboolean start)
{
    if (!sGattIf) return;
    sGattIf->client->listen(client_if, start);
}
这里的映射关系我就不再介绍了,上一篇文章已经都介绍一次了,我们这里直接看看listen函数:

static bt_status_t btif_gattc_listen(int client_if, bool start)
{
    CHECK_BTGATT_INIT();
    btif_gattc_cb_t btif_cb;
    btif_cb.client_if = (uint8_t) client_if;
    btif_cb.start = start ? 1 : 0;
    return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_LISTEN,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}
直接看实现:
static void btgattc_handle_event(uint16_t event, char* p_param)
{
     ……
    switch (event)
    {  

case BTIF_GATTC_LISTEN:
#if (defined(BLE_PERIPHERAL_MODE_SUPPORT) && (BLE_PERIPHERAL_MODE_SUPPORT == TRUE))
            BTA_GATTC_Listen(p_cb->client_if, p_cb->start, NULL);
#else
            BTA_GATTC_Broadcast(p_cb->client_if, p_cb->start);
#endif
            break;
          ……
目前我用的设备BLE_PERIPHERAL_MODE_SUPPORT是true,所以我们进入BTA_GATTC_Listen:

void BTA_GATTC_Listen(tBTA_GATTC_IF client_if, BOOLEAN start, BD_ADDR_PTR target_bda)
{
    tBTA_GATTC_API_LISTEN  *p_buf;  

    if ((p_buf = (tBTA_GATTC_API_LISTEN *) GKI_getbuf((UINT16)(sizeof(tBTA_GATTC_API_LISTEN) + BD_ADDR_LEN))) != NULL)
    {
        p_buf->hdr.event = BTA_GATTC_API_LISTEN_EVT;  

        p_buf->client_if = client_if;
        p_buf->start = start;
        if (target_bda)
        {
            p_buf->remote_bda = (UINT8*)(p_buf + 1);
            memcpy(p_buf->remote_bda, target_bda, BD_ADDR_LEN);
        }
        else
            p_buf->remote_bda = NULL;  

        bta_sys_sendmsg(p_buf);
    }
    return;
}
这里是开始进入广播且监听一个client设备的连接请求。这里要注意一下event时间,这个时间会被触发:

BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{
      ……
      case BTA_GATTC_API_LISTEN_EVT:
            bta_gattc_listen(p_cb, (tBTA_GATTC_DATA *) p_msg);
            break;
      ……
[cpp] view plaincopy
void bta_gattc_listen(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA * p_msg)
{
    tBTA_GATTC_RCB      *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_listen.client_if);
    tBTA_GATTC          cb_data;
    UNUSED(p_cb);  

    cb_data.reg_oper.status = BTA_GATT_ERROR;
    cb_data.reg_oper.client_if = p_msg->api_listen.client_if;  

    if (p_clreg == NULL)
    {
        APPL_TRACE_ERROR("bta_gattc_listen failed, unknown client_if: %d",
                            p_msg->api_listen.client_if);
        return;
    }
    /* mark bg conn record */
    if (bta_gattc_mark_bg_conn(p_msg->api_listen.client_if,
                               (BD_ADDR_PTR) p_msg->api_listen.remote_bda,
                               p_msg->api_listen.start,
                               TRUE))
    {
        if (!GATT_Listen(p_msg->api_listen.client_if,
                         p_msg->api_listen.start,
                         p_msg->api_listen.remote_bda))
        {
            APPL_TRACE_ERROR("Listen failure");
            (*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data);
        }
        else
        {
            cb_data.status = BTA_GATT_OK;  

            (*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data);  

            if (p_msg->api_listen.start)
            {
                /* if listen to a specific target */
                if (p_msg->api_listen.remote_bda != NULL)
                {  

                    /* if is a connected remote device */
                    if (L2CA_GetBleConnRole(p_msg->api_listen.remote_bda) == HCI_ROLE_SLAVE &&
                        bta_gattc_find_clcb_by_cif(p_msg->api_listen.client_if,
                                                   p_msg->api_listen.remote_bda,
                                                   BTA_GATT_TRANSPORT_LE) == NULL)
                    {  

                        bta_gattc_init_clcb_conn(p_msg->api_listen.client_if,
                                                p_msg->api_listen.remote_bda);
                    }
                }
                /* if listen to all */
                else
                {
                    APPL_TRACE_ERROR("Listen For All now");
                    /* go through all connected device and send
                    callback for all connected slave connection */
                    bta_gattc_process_listen_all(p_msg->api_listen.client_if);
                }
            }
        }
    }
}
到这里,剩下的全是连接相关,我自己都有点绕,所以暂时就不讲解了。这块后期再补上。

 private void setAdvertisingData(AdvertiseClient client, AdvertiseData data,
               boolean isScanResponse) {
           if (data == null) {
               return;
           }
           boolean includeName = data.getIncludeDeviceName();
           boolean includeTxPower = data.getIncludeTxPowerLevel();
           int appearance = 0;
           byte[] manufacturerData = getManufacturerData(data);  

           byte[] serviceData = getServiceData(data);
           byte[] serviceUuids;
           if (data.getServiceUuids() == null) {
               serviceUuids = new byte[0];
           } else {
               ByteBuffer advertisingUuidBytes = ByteBuffer.allocate(
                       data.getServiceUuids().size() * 16)
                       .order(ByteOrder.LITTLE_ENDIAN);
               for (ParcelUuid parcelUuid : data.getServiceUuids()) {
                   UUID uuid = parcelUuid.getUuid();
                   // Least significant bits first as the advertising UUID should be in
                   // little-endian.
                   advertisingUuidBytes.putLong(uuid.getLeastSignificantBits())
                           .putLong(uuid.getMostSignificantBits());
               }
               serviceUuids = advertisingUuidBytes.array();
           }
           if (mAdapterService.isMultiAdvertisementSupported()) {
               Log.i(TAG, "gattClientSetAdvDataNative(" + client.clientIf + ",...);");
               gattClientSetAdvDataNative(client.clientIf, isScanResponse, includeName,
                       includeTxPower, appearance,
                       manufacturerData, serviceData, serviceUuids);
           } else {
               Log.i(TAG, "gattSetAdvDataNative(" + client.clientIf + ",...);");
               gattSetAdvDataNative(client.clientIf, isScanResponse, includeName,
                       includeTxPower, 0, 0, appearance,
                       manufacturerData, serviceData, serviceUuids);
           }
       }

static void gattSetAdvDataNative(JNIEnv *env, jobject object, jint client_if,
        jboolean setScanRsp, jboolean inclName, jboolean inclTxPower, jint minInterval,
        jint maxInterval, jint appearance, jbyteArray manufacturerData, jbyteArray serviceData,
        jbyteArray serviceUuid)
{
    if (!sGattIf) return;
    jbyte* arr_data = env->GetByteArrayElements(manufacturerData, NULL);
    uint16_t arr_len = (uint16_t) env->GetArrayLength(manufacturerData);  

    jbyte* service_data = env->GetByteArrayElements(serviceData, NULL);
    uint16_t service_data_len = (uint16_t) env->GetArrayLength(serviceData);  

    jbyte* service_uuid = env->GetByteArrayElements(serviceUuid, NULL);
    uint16_t service_uuid_len = (uint16_t) env->GetArrayLength(serviceUuid);  

    sGattIf->client->set_adv_data(client_if, setScanRsp, inclName, inclTxPower,
        minInterval, maxInterval, appearance, arr_len, (char*)arr_data,
        service_data_len, (char*)service_data, service_uuid_len,
        (char*)service_uuid);  

    env->ReleaseByteArrayElements(manufacturerData, arr_data, JNI_ABORT);
    env->ReleaseByteArrayElements(serviceData, service_data, JNI_ABORT);
    env->ReleaseByteArrayElements(serviceUuid, service_uuid, JNI_ABORT);
}
那我们就来主要看看set_adv_data函数:

static bt_status_t btif_gattc_set_adv_data(int client_if, bool set_scan_rsp, bool include_name,
                bool include_txpower, int min_interval, int max_interval, int appearance,
                uint16_t manufacturer_len, char* manufacturer_data,
                uint16_t service_data_len, char* service_data,
                uint16_t service_uuid_len, char* service_uuid)
{
    CHECK_BTGATT_INIT();
    bt_status_t status =0;  

    btif_adv_data_t adv_data;  

    btif_gattc_adv_data_packager(client_if, set_scan_rsp, include_name,
        include_txpower, min_interval, max_interval, appearance, manufacturer_len,
        manufacturer_data, service_data_len, service_data, service_uuid_len, service_uuid,
        &adv_data);  

    status = btif_transfer_context(btgattc_handle_event, BTIF_GATTC_SET_ADV_DATA,
                       (char*) &adv_data, sizeof(btif_adv_data_t), NULL);  

    if (NULL != adv_data.p_service_data)
        GKI_freebuf(adv_data.p_service_data);  

    if (NULL != adv_data.p_service_uuid)
        GKI_freebuf(adv_data.p_service_uuid);  

    if (NULL != adv_data.p_manufacturer_data)
        GKI_freebuf(adv_data.p_manufacturer_data);  

    return status;
}
其中btif_gattc_adv_data_packager直接到了GKI,所以我们来看一下btgattc_handle_event触发的event事件:

case BTIF_GATTC_SET_ADV_DATA:
{
    btif_adv_data_t *p_adv_data = (btif_adv_data_t*) p_param;
    int cbindex = CLNT_IF_IDX;
    if (cbindex >= 0 && NULL != p_adv_data)
    {
        btgatt_multi_adv_common_data *p_multi_adv_data_cb = btif_obtain_multi_adv_data_cb();
        if (!btif_gattc_copy_datacb(cbindex, p_adv_data, false))
            return;  

        if (!p_adv_data->set_scan_rsp)
        {
            BTA_DmBleSetAdvConfig(p_multi_adv_data_cb->inst_cb[cbindex].mask,
                &p_multi_adv_data_cb->inst_cb[cbindex].data, bta_gattc_set_adv_data_cback);
        }
        else
        {
            BTA_DmBleSetScanRsp(p_multi_adv_data_cb->inst_cb[cbindex].mask,
                &p_multi_adv_data_cb->inst_cb[cbindex].data, bta_gattc_set_adv_data_cback);
        }
        break;
    }
}
这里我们主要是走了BTA_DmBleSetAdvConfig:

void BTA_DmBleSetAdvConfig (tBTA_BLE_AD_MASK data_mask, tBTA_BLE_ADV_DATA *p_adv_cfg,
                            tBTA_SET_ADV_DATA_CMPL_CBACK *p_adv_data_cback)
{
    tBTA_DM_API_SET_ADV_CONFIG  *p_msg;  

    if ((p_msg = (tBTA_DM_API_SET_ADV_CONFIG *)
        GKI_getbuf(sizeof(tBTA_DM_API_SET_ADV_CONFIG))) != NULL)
    {
        p_msg->hdr.event = BTA_DM_API_BLE_SET_ADV_CONFIG_EVT;
        p_msg->data_mask = data_mask;
        p_msg->p_adv_data_cback = p_adv_data_cback;
        p_msg->p_adv_cfg = p_adv_cfg;  

        bta_sys_sendmsg(p_msg);
    }
}
这里实际会走到
void bta_dm_ble_set_adv_config (tBTA_DM_MSG *p_data)
{
    tBTA_STATUS status = BTA_FAILURE;  

    if (BTM_BleWriteAdvData(p_data->ble_set_adv_data.data_mask,
                        (tBTM_BLE_ADV_DATA *)p_data->ble_set_adv_data.p_adv_cfg) == BTM_SUCCESS)
    {
        status = BTA_SUCCESS;
    }  

    if (p_data->ble_set_adv_data.p_adv_data_cback)
        (*p_data->ble_set_adv_data.p_adv_data_cback)(status);
}
tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data)
{
    tBTM_BLE_LOCAL_ADV_DATA *p_cb_data = &btm_cb.ble_ctr_cb.inq_var.adv_data;
    UINT8  *p;
    tBTM_BLE_AD_MASK   mask = data_mask;  

    BTM_TRACE_EVENT ("BTM_BleWriteAdvData ");  

    if (!HCI_LE_HOST_SUPPORTED(btm_cb.devcb.local_lmp_features[HCI_EXT_FEATURES_PAGE_1]))
        return BTM_ILLEGAL_VALUE;  

    memset(p_cb_data, 0, sizeof(tBTM_BLE_LOCAL_ADV_DATA));
    p = p_cb_data->ad_data;
    p_cb_data->data_mask = data_mask;  

    p_cb_data->p_flags = btm_ble_build_adv_data(&mask, &p, p_data);  

    p_cb_data->p_pad = p;  

    if (mask != 0)
    {
        BTM_TRACE_ERROR("Partial data write into ADV");
    }  

    p_cb_data->data_mask &= ~mask;  

    if (btsnd_hcic_ble_set_adv_data((UINT8)(p_cb_data->p_pad - p_cb_data->ad_data),
                                    p_cb_data->ad_data))
        return BTM_SUCCESS;
    else
        return BTM_NO_RESOURCES;  

}
BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data)
{
    BT_HDR *p;
    UINT8 *pp;  

    if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1)) == NULL)
        return (FALSE);  

    pp = (UINT8 *)(p + 1);  

    p->len    = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1;
    p->offset = 0;  

    UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_DATA);
    UINT8_TO_STREAM  (pp, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1);  

    memset(pp, 0, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA);  

    if (p_data != NULL && data_len > 0)
    {
        if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA)
            data_len = HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA;  

        UINT8_TO_STREAM (pp, data_len);  

        ARRAY_TO_STREAM (pp, p_data, data_len);
    }
    btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID,  p);  

    return (TRUE);
}
后面已经直接在btu通信了,我们也就不深入了。
所以步骤跑完,在callback就会获得onSuccess中。

广播就结束了,后面我会继续更新Central相关流程。
时间: 2024-10-10 16:35:57

android5.0(Lollipop) BLE Peripheral深入理解系统篇之提高篇的相关文章

android5.0(Lollipop) BLE Peripheral牛刀小试

转载请表明作者:http://blog.csdn.net/lansefeiyang08/article/details/46468743 知道Android L对蓝牙对了一些改进.包含加入A2dp sink.HFP client.BLE Peripheral功能等等. 我花了一天多时间对Android L BLE Peripheral SDK进行了研究,网上的资料非常少,有一个介绍的还不够清晰,所以就自己写了一个測试应用.希望能够对理解BLE Peripheral有一定的帮助. 此贴主要以解说代

android5.0(Lollipop) BLE Central牛刀小试

转载请表明作者:http://blog.csdn.net/lansefeiyang08/article/details/46482073 昨天写了android L BLE Peripheral的简单使用,今天讲一下BLE Central的更新. 搞过android4.4的人肯定对BluetoothAdapter的startLeScan函数不会陌生,但是在android L已经弃用此接口,但是为了兼容以前的版本,这个接口还是可以使用的.但是谷歌已经单独拿出来android.bluetooth.l

Android5.0(lollipop)新特性介绍(一)

今年6月的Google I/O大会上,Android L的初次见面我相信让会让很多android粉丝有些小激动和小期待,当然作为开发者的我来说,激动不言而喻,毕竟这是自08年以来改变最大的一个版本.新的设计语言(Material Design),5000多个新增api.废话不多说,今天要说的基本都是在Android5.0中很常见,也算是对自己学习的一种记录. 1.CardView 顾名思义,CardView 卡片视图,继承自framelayout,可以通过设置圆角以及阴影来展示带有像卡片一样的效

AOSP Android5.0 lollipop 源码同步更新方法

repo init -u https://android.googlesource.com/platform/manifest -b android-5.0.0_r2 repo sync -j16 或者 repo init -u https://android.googlesource.com/platform/manifest -b lollipop-release repo sync -j16

ubuntu下 nexus4 刷 android5.0(Lollipop)

1.下载5.0的安装包,建议去别人上传的网盘资源下载,官方需要科学上网,速度实在够慢 2.terminal下安装fastboot,敲一下,会提示你apt-get的命令 3.USB连接手机,terminal sudo fastboot reboot-bootloader 让手机进入bootloader界面:sudo fastboot oem unlock unlock你的nexus4: 4.解压安装包,terminal cd到解压后的目录,执行flash-all.sh,安装完成会重启,拔掉USB,

Android5.0 Lollipop正式发布!

今天早些时候,谷歌正式发布了Android 5.0,一同亮相的还有Nexus 6等新品. 跟之前传闻的一样,Android 5.0之所以叫L,其代号是 Lollipop棒棒糖,除了32位版本外,还有64位. 至于Android 5.0的新特性,之前谷歌I/O大会上都已经说的很清楚,比如ART作为默认选项,提高设备续航,新的多任务以及全新的界面设计(material design),当然这次还加入了新的64位版本. 对于5.0来说,谷歌最大的愿景是,统一Android平台.除了Nexus 6和Ne

ubuntu14.10上编译Android5.0.2源码

2015年04月14日 Android 暂无评论 阅读2064次ubuntu14.10上编译Android5.0.2源码 前面一篇文章说到了ubuntu14.10上Android5.0.2源码下载的具体步骤,这篇文章说的是ubuntu14.10上编译Android5.0.2源码的那些事. 1.先安装jdk 这里我使用的是openjdk,网上很多人关于jdk到底使用jdk5.jdk6.jdk7还是openjdk一直在争论不休,我选择的是openjdk7,最终证明是可以编译通过的. sudo apt

Android5.0系统的优缺点

Android L(5.0)正式定名为 Lollipop(棒棒糖).安卓已经六岁了,也总算有一次重大改观了.安卓5.0 Lollipop带来了全新的,扁平化的外观,更好的通知中心,重新设计的核心应用,并提升了在安卓设备上的性能表现--增加了一些以前所缺失的重要应用类别. 首先来看一下Android L相比之前的版本有什么新鲜的东西. 原文博客请参考:点击打开链接 一:自定义通知中心 Lollipop为用户带来了对通知中心前所未有的控制性,每一个应用程序都可以在通知中心进行单独的设置,并且只有在用

Android5.0以上系统的移动网络开关

笔者近期遇到一个非常有意思的bug,贴出来和大家分享下. 那是一个温暖的早晨,阳光晒得人非常舒服.一封bug邮件像一片叶子飘到我的邮箱. 一番交流.笔者确认负责的Widget开关在Android5.0以上系统没有作用.相信非常多做过移动网络开关的朋友都知道.传统的方法是在ConnectivityManager中通过反射两个方法setMobileDataEnabled和getMobileDataEnabled来控制移动网络开和关的. /** * Gets the value of the sett