1. 程式人生 > >Android6.0藍芽探索旅程

Android6.0藍芽探索旅程

廢話不說,直接擼程式碼。
模組一:模擬手機設定介面開啟藍芽操作逐步分析。

Step1

設定介面
對應檔案packages/apps/Settings/SettingsActivity.java


    private static final String[] ENTRY_FRAGMENTS = {
           ......
            BluetoothSettings.class.getName(),
            ......
 }```

藍芽作為眾多設定的一個,也被新增到陣列中了。

Step2

藍芽設定介面這裡寫圖片描述
對應檔案ackages/apps/Settings/bluetooth/BluetoothSettings.java
public class BluetoothSettings extends DeviceListPreferenceFragment implements Indexable {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
……….
final SettingsActivity activity = (SettingsActivity) getActivity();
mSwitchBar = activity.getSwitchBar();
mBluetoothEnabler = new BluetoothEnabler(activity, mSwitchBar);
mBluetoothEnabler.setupSwitchBar();
}

}
繼承自fragment,添加了一個switchBar。嗯,接下來分析BluetoothEnabler這個檔案時如何來控制藍芽的開關的。

Step3

檔案目錄:packages/apps/Settings/bluetooth/BluetoothEnabler.java
`public void onSwitchChanged(Switch switchView, boolean isChecked) {

    if (isChecked &&
            !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
        Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
        // Reset switch to off
        switchView.setChecked(false);
    }

    MetricsLogger.action(mContext, MetricsLogger.ACTION_BLUETOOTH_TOGGLE, isChecked);

    Log.d(TAG, "mUpdateStatusOnly is " + mUpdateStatusOnly);
    /// M: if receive bt status changed broadcast, do not need enable/disable bt.
    if (mLocalAdapter != null && !mUpdateStatusOnly) {
        mLocalAdapter.setBluetoothEnabled(isChecked);
    }
    mSwitch.setEnabled(false);
}`

Step4

呼叫bluetoothAdapter的setBluetoothEnabled(isChecked)方法,
boolean success = enabled ? mAdapter.enable() : mAdapter.disable()
終於到核心類BluetoothAdapter了。
目錄來了一個大的改變從packages目錄到了frameworks目錄。
frameworks/base/core/java/android/bluetooth/bluetoothAdapter.java

 @SystemApi
    public boolean enableBLE() {
        if
(!isBleScanAlwaysAvailable()) return false; if (isLeEnabled() == true) { if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); try { mManagerService.updateBleAppCount(mToken, true); } catch (RemoteException e) { Log.e(TAG, "", e); } return true; } try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); return mManagerService.enable(); } catch (RemoteException e) { Log.e(TAG, "", e); } return false; }

Step5

重點關注 return mManagerService.enable(); mManagerService的建立時在BluetoothAdapter中完成的

  public static synchronized BluetoothAdapter getDefaultAdapter() {
        if (sAdapter == null) {
            IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
            if (b != null) {
                IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
                sAdapter = new BluetoothAdapter(managerService);
            } else {
                Log.e(TAG, "Bluetooth binder is null");
            }
        }
        return sAdapter;
    }

    /**
     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
     */
    BluetoothAdapter(IBluetoothManager managerService) {

        if (managerService == null) {
            throw new IllegalArgumentException("bluetooth manager service is null");
        }
        try {
            mService = managerService.registerAdapter(mManagerCallback);
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        mManagerService = managerService;
        mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
        mToken = new Binder();
    }

我們可以看到,getDefaultAdapter方法和構造方法配合著使用。當獲取例項時,mManagerService也例項化了。首先通過ServiceManager的getService返回一個binder型別的物件,然後獲取到遠端物件的介面managerService ,來進行遠端呼叫。這樣我們直接檢視frameworks/base/core/java/android/bluetooth/BluetoothManagerService 的enable方法。

Step5

在這裡前面最終還是呼叫了, sendEnableMsg(false);傳送了MESSAGE_ENABLE訊息,我們看看,哪個handler接收了這個訊息。

  public boolean enable() {
        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
            (!checkIfCallerIsForegroundUser())) {
            Log.w(TAG,"enable(): not allowed for non-active and non system user");
            return false;
        }

        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                "Need BLUETOOTH ADMIN permission");
        if (DBG) {
            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
                    " mBinding = " + mBinding);
        }
        /// M: MoMS permission check @{
        if (MobileManagerUtils.isSupported()) {
            checkEnablePermission();
            return true;
        }
        /// @}
        synchronized(mReceiver) {
            mQuietEnableExternal = false;
            mEnableExternal = true;
            // waive WRITE_SECURE_SETTINGS permission check
            sendEnableMsg(false);
        }
        if (DBG) Log.d(TAG, "enable returning");
        return true;
    }

   private void sendEnableMsg(boolean quietMode) {
        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
                             quietMode ? 1 : 0, 0));
    }

Step6

BluetoothManagerService有個內部類BluetoothHandler.java,繼續發訊息,裡面有個doBind方法,發現最終拉起一個Service,看看ServiceConnection的onServiceConected方法,發現又是發訊息MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED,裡面會根據AdapterService和GattService的差異區別傳送,最終傳一個binder物件到message中,繼續看看這個訊息傳送的目的地,他在BluetoothHandler中,這裡也是把之前的binder物件轉化成呼叫遠端服務的介面,所以最終我們要看在
AdapterService和GattService是如何處理的。

        public void handleMessage(Message msg) {
        ...
          case MESSAGE_ENABLE:
   mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                    mEnable = true;
                    handleEnable(msg.arg1 == 1);
                    break;
             case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
                {
                 。。。。
                    IBinder service = (IBinder) msg.obj;
                    synchronized(mConnection) {
                        if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                   mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
                            onBluetoothGattServiceUp();
                            break;
                        } // else must be SERVICE_IBLUETOOTH
                        //Remove timeout                      mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                        mBinding = false;
                        mBluetooth = IBluetooth.Stub.asInterface(service);
                        try {
                            boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
                                Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
                            if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) {
                                Log.e(TAG,"IBluetooth.configHciSnoopLog return false");
                            }
                        } catch (RemoteException e) {
                            Log.e(TAG,"Unable to call configHciSnoopLog", e);
                        }
.....                      
                    break;
                }          
          case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                {
                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
                    synchronized(mConnection) {
                        if (msg.arg1 == SERVICE_IBLUETOOTH) {
                            // if service is unbinded already, do nothing and return
                            if (mBluetooth == null) break;
                            mBluetooth = null;
                        } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                            mBluetoothGatt = null;
                            break;
                        } else {
                            Log.e(TAG, "Bad msg.arg1: " + msg.arg1);
                            break;
                        }
                    }
        ...
        }

            private void handleEnable(boolean quietMode) {
        synchronized(mConnection) {     
            if ((mBluetooth == null) && (!mBinding)) {
                Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
       mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                mConnection.setGetNameAddressOnly(false);
                Intent i = new Intent(IBluetooth.class.getName());
                if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                        UserHandle.CURRENT)) {
                   }
    }

 boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
        intent.setComponent(comp);
        if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
            Log.e(TAG, "Fail to bind to: " + intent);
            return false;
        }
        return true;
    }

  private class BluetoothServiceConnection implements ServiceConnection {
。。。。。
        public void onServiceConnected(ComponentName className, IBinder service) {
            if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
            // TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
                msg.arg1 = SERVICE_IBLUETOOTH;
                // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
                msg.arg1 = SERVICE_IBLUETOOTHGATT;
            } else {
                Log.e(TAG, "Unknown service connected: " + className.getClassName());
                return;
            }
            msg.obj = service;
            mHandler.sendMessage(msg);
        }

        。。。。
    }

Step7

上面已經說到,要找AdapterService和GattService裡繼續追蹤。接下來重點考慮AdapterService,他的路徑在Packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService,所以又從framework層調到了app層。從何處入手了,Step6中說了拉起服務的事,其實後面拉起服務後還會有一段程式碼,啟動藍芽,我們看看AdapterService的enable方法。


                //Enable bluetooth
                try {
                    if (!mQuietEnable) {
                        if(!mBluetooth.enable()) {
                            Log.e(TAG,"IBluetooth.enable() returned false");
                        }
                    }
                    else {
                        if(!mBluetooth.enableNoAutoConnect()) {
                            Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
                        }
                    }
                } catch (RemoteException e) {
                    Log.e(TAG,"Unable to call enable()",e);
                }


public class AdapterService extends Service {
...
public synchronized boolean enable(boolean quietMode) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");

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

Step8

嗯,繼續發訊息,AdapterState.BLE_TURN_ON
Packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java,裡面有很多stateMachine,也有對訊息的監聽這裡,我也有點蒙圈,不按照套路出牌,最終要呼叫adapterService.enableNative()的native方法,進行jni通訊。

 case BLE_TURN_ON:
                    if (isTurningOff || isBleTurningOff) {
                        infoLog("Deferring BLE_TURN_ON request...");
                        deferMessage(msg);
                    }
                    break;
  case BLE_STARTED:
                    //Remove start timeout
                    removeMessages(BLE_START_TIMEOUT);

                    //Enable
                    if (!adapterService.enableNative()) {
                        errorLog("Error while turning Bluetooth on");
                        notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
                        transitionTo(mOffState);
                    } else {
                        sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
                    }
                    break;

Step9

從應用層調到了藍芽協議棧了,我們看看它的c++層是如何操作的。
Packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp,藍芽驅動物件sBluetoothInterface的enable方法。好吧我再也分析不下去了。

static jboolean enableNative(JNIEnv* env, jobject obj) {  
        ALOGV("%s:",__FUNCTION__);  

        jboolean result = JNI_FALSE;  
        if (!sBluetoothInterface) return result;  

        int ret = sBluetoothInterface->enable();  
        result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  
        return result;  
    }  

總的來說博通寫的藍芽模組的內容涉及了好多message資訊的處理。下面一篇我會對藍芽檔案傳輸進行分析。