太通透了,Android 流程分析 蓝牙enable流程(应用层/Framework/Service层)

 零. 前言

由于Bluedroid的介绍文档有限,以及对Android的一些基本的知识需要了(Android 四大组件/AIDL/Framework/Binder机制/JNI/HIDL等),加上需要掌握的语言包括Java/C/C++等,加上网络上其实没有一个完整的介绍Bluedroid系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员,都有不小的难度,学习曲线也相对较陡,所以我有了这个想法,专门对Bluedroid做一个系统性的介绍,尽可能的涵盖所有内容。

-------------------------------------------------------------------------------------------------------------------------

蓝牙视频教程(跟韦东山老师合作), 其中专题21就是专门针对Bluedroid做的系统介绍

https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22329603896.20.5aeb41f98e267j&id=693788592796

--------------------------------------------------------------------------------------------------------------------------

我们之前介绍过Android的架构,我们本节就介绍下如下图部分:

整个AOSP蓝牙的代码如下:

备注:不同的Android版本下可能路径不同,需要自行去查看下

一. 蓝牙系统服务

这块代码主要在/frameworks/base/services/java/com/android/server/SystemServer.java

在Android系统启动过程中,SystemServer是第一个被启动的进程。当Android设备被开机后,内核会创建一个名为init的进程,init进程会启动Zygote进程,Zygote进程会启动SystemServer进程。因此,SystemServer是整个Android系统启动的核心进程,它负责初始化并启动大部分系统服务和应用程序,是整个Android系统的主要启动入口。

具体来说,SystemServer主要完成以下几个任务:

  • 启动和初始化系统服务:SystemServer会启动和初始化大部分系统服务,例如ActivityManagerService、WindowManagerService、PackageManagerService、PowerManagerService等等。这些服务会在启动过程中被创建并注册到系统服务中心,供其他应用程序和服务调用。
  • 启动和初始化核心应用程序:SystemServer会启动和初始化Android系统中的核心应用程序,例如SystemUI、Settings等等。这些应用程序会在启动过程中被创建并运行,提供各种用户界面和功能。
  • 加载和初始化系统属性:SystemServer会加载和初始化/system/build.prop文件中定义的系统属性,例如设备型号、厂商信息等等。这些属性可以在系统运行时被访问和修改。
  • 启动Adb守护进程:SystemServer会启动Adb守护进程,使得开发者可以通过adb工具来访问设备。

总之,SystemServer是整个Android系统启动过程中的核心进程,负责启动和初始化大部分系统服务和应用程序,为整个系统的运行提供基础支持。SystemServer的启动时间通常在内核启动之后的几秒钟内,具体时间取决于设备的硬件性能和系统配置。

/frameworks/base/services/java/com/android/server/SystemServer.java

SystemServer启动代码通过main函数运行。

/**
* The main entry point from zygote.
*/
public static void main(String[] args) {new SystemServer().run();
}

SystemServer().run();执行startOtherServices(t);

/frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {...     // Start services.try {traceBeginAndSlog("StartServices");startBootstrapServices();startCoreServices();startOtherServices();SystemServerInitThreadPool.shutdown();} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;} finally {traceEnd();}...// Loop forever.Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

然后startOtherServices(t);执行如下代码,开启Bluetooth service类

/frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {Slog.i(TAG, "No Bluetooth Service (factory test)");} else if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");} else {t.traceBegin("StartBluetoothService");// 启动蓝牙fwk manager servicemSystemServiceManager.startService(BluetoothService.class);t.traceEnd();}...
}

/frameworks/base/services/core/java/com/android/server/BluetoothService.java

public BluetoothService(Context context) {super(context);mBluetoothManagerService = new BluetoothManagerService(context);
}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

BluetoothManagerService(Context context) {Slog.e(TAG, "new BluetoothManagerService");// BluetoothManagerService的私有类,主要用于处理一些messagemHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context;mWirelessConsentRequired = context.getResources().getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);mCrashes = 0;mBluetooth = null;mBluetoothBinder = null;mBluetoothGatt = null;mBinding = false;mUnbinding = false;mEnable = false;mState = BluetoothAdapter.STATE_OFF;mQuietEnableExternal = false;mEnableExternal = false;mAddress = null;mName = null;mErrorRecoveryRetryCounter = 0;mContentResolver = context.getContentResolver();// Observe BLE scan only mode settings change.registerForBleScanModeChange();mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();mIsHearingAidProfileSupported = context.getResources().getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported);// TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtilsString value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);if (!TextUtils.isEmpty(value)) {boolean isHearingAidEnabled = Boolean.parseBoolean(value);Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);if (isHearingAidEnabled && !mIsHearingAidProfileSupported) {// Overwrite to enable support by FeatureFlagmIsHearingAidProfileSupported = true;}}IntentFilter filter = new IntentFilter();filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);filter.addAction(Intent.ACTION_SETTING_RESTORED);filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);mContext.registerReceiver(mReceiver, filter);loadStoredNameAndAddress();if (isBluetoothPersistedStateOn()) {if (DBG) {Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");}mEnableExternal = true;}String airplaneModeRadios =Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);if (airplaneModeRadios == null || airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(this, IoThread.get().getLooper(), context);}int systemUiUid = -1;// Check if device is configured with no home screen, which implies no SystemUI.boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);if (!noHome) {PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),MATCH_SYSTEM_ONLY, USER_SYSTEM);}if (systemUiUid >= 0) {Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));} else {// Some platforms, such as wearables do not have a system ui.Slog.w(TAG, "Unable to resolve SystemUI's UID.");}mSystemUiUid = systemUiUid;}

到此为止,BluetoothManagerService 就停止在这里等待Binder connection

二. Settings开启蓝牙的流程

其中Apps就是我们所说的Settings app或者你自己写的app

Settings的代码路径如下:packages\apps\Settings

蓝牙的framework 路径如下:frameworks\base\core\java\android\bluetooth

1. 下行方向

我所谓的下行方向就是通过应用层调用到fwk到service到system bt到hidl service到controller这个方向!

framework的函数你可以理解为是API,那么Settings apk或者你自己写的就是调用fwk的api

首先在创建BluetoothEnabler的类中,会获取fwk的BluetoothAdapter class的静态方法getDefaultAdapter获取adapter. 这个api就相当于调用的函数。

/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothEnabler.java

public BluetoothEnabler(Context context, SwitchWidgetController switchController,MetricsFeatureProvider metricsFeatureProvider, int metricsEvent,RestrictionUtils restrictionUtils) {...mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (mBluetoothAdapter == null) {// Bluetooth is not supportedmSwitchController.setEnabled(false);}...
}

/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

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;
}

其中IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);

如果蓝牙管理服务(BluetoothManagerService)已经在系统中运行,并且它的服务代理已经被注册到ServiceManager中,那么调用 ServiceManager.getService() 将会直接返回这个已经存在的代理对象,而不会立即触发Binder通信。

后面调用蓝牙enable跟disable方法

/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothEnabler.java

private boolean setBluetoothEnabled(boolean isEnabled) {return isEnabled ? mBluetoothAdapter.enable() : mBluetoothAdapter.disable();}

我们可以看下aidl接口

/system/bt/binder/android/bluetooth/IBluetoothManager.aidl

interface IBluetoothManager
{IBluetooth registerAdapter(in IBluetoothManagerCallback callback);void unregisterAdapter(in IBluetoothManagerCallback callback);void registerStateChangeCallback(in IBluetoothStateChangeCallback callback);void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);boolean isEnabled();boolean enable(String packageName);boolean enableNoAutoConnect(String packageName);boolean disable(String packageName, boolean persist);int getState();IBluetoothGatt getBluetoothGatt();boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);String getAddress();String getName();boolean isBleScanAlwaysAvailable();int updateBleAppCount(IBinder b, boolean enable, String packageName);boolean isBleAppPresent();boolean isHearingAidProfileSupported();
}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

public boolean enable(String packageName) throws RemoteException {...synchronized (mReceiver) {mQuietEnableExternal = false;mEnableExternal = true;// waive WRITE_SECURE_SETTINGS permission checksendEnableMsg(false,BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);}...
}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

private void sendEnableMsg(boolean quietMode, int reason, String packageName) {mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));addActiveLog(reason, packageName, true);mLastEnabledTime = SystemClock.elapsedRealtime();
}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

public void handleMessage(Message msg) 
{switch(msg.what){...case MESSAGE_ENABLE:handleEnable(mQuietEnable);....}
}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

private void handleEnable(boolean quietMode) {mQuietEnable = quietMode;try {mBluetoothLock.writeLock().lock();if ((mBluetooth == null) && (!mBinding)) {//Start bind timeout and bindMessage timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);Intent i = new Intent(IBluetooth.class.getName());if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,UserHandle.CURRENT)) {mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);} else {mBinding = true;}} else if (mBluetooth != null) {//Enable bluetoothtry {if (!mQuietEnable) {if (!mBluetooth.enable()) {Slog.e(TAG, "IBluetooth.enable() returned false");}} else {if (!mBluetooth.enableNoAutoConnect()) {Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");}}} catch (RemoteException e) {Slog.e(TAG, "Unable to call enable()", e);}}} finally {mBluetoothLock.writeLock().unlock();}
}

这个会触发bind,那么被绑定的服务在哪里声明的呢?

/packages/apps/Bluetooth/AndroidManifest.xml

bond后会调用这个函数onServiceConnected

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

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

另外,绑定AdapterService, 这个会触发两个动作:

1)AdapterService静态方法调用

2)AdapterService onCreate方法调用

我们先说第一点哈·,AdapterService静态方法调用

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

static {System.loadLibrary("bluetooth_jni");classInitNative();
}

System.loadLibrary("bluetooth_jni");

加载bluetooth_jni动态库会触发JNI_OnLoad,这个方法就是把jni的cpp方法注册到java中,这样jave层就可以直接调用jni的cpp代码了,比如后面调用的classInitNative就是jni cpp代码,我们来简单看下一个注册过程

packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static JNINativeMethod sMethods[] = {/* name, signature, funcPtr */{"classInitNative", "()V", (void*)classInitNative},{"initNative", "(ZZZ)Z", (void*)initNative},{"cleanupNative", "()V", (void*)cleanupNative},{"enableNative", "()Z", (void*)enableNative},{"disableNative", "()Z", (void*)disableNative},{"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative},{"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative},{"getAdapterPropertyNative", "(I)Z", (void*)getAdapterPropertyNative},{"getDevicePropertyNative", "([BI)Z", (void*)getDevicePropertyNative},{"setDevicePropertyNative", "([BI[B)Z", (void*)setDevicePropertyNative},{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},{"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative},{"createBondNative", "([BI)Z", (void*)createBondNative},{"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;)Z",(void*)createBondOutOfBandNative},{"removeBondNative", "([B)Z", (void*)removeBondNative},{"cancelBondNative", "([B)Z", (void*)cancelBondNative},{"getConnectionStateNative", "([B)I", (void*)getConnectionStateNative},{"pinReplyNative", "([BZI[B)Z", (void*)pinReplyNative},{"sspReplyNative", "([BIZI)Z", (void*)sspReplyNative},{"getRemoteServicesNative", "([B)Z", (void*)getRemoteServicesNative},{"getSocketManagerNative", "()Landroid/os/IBinder;",(void*)getSocketManagerNative},{"setSystemUiUidNative", "(I)V", (void*)setSystemUiUidNative},{"setForegroundUserIdNative", "(I)V", (void*)setForegroundUserIdNative},{"alarmFiredNative", "()V", (void*)alarmFiredNative},{"readEnergyInfo", "()I", (void*)readEnergyInfo},{"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V",(void*)dumpNative},{"dumpMetricsNative", "()[B", (void*)dumpMetricsNative},{"factoryResetNative", "()Z", (void*)factoryResetNative},{"interopDatabaseClearNative", "()V", (void*)interopDatabaseClearNative},{"interopDatabaseAddNative", "(I[BI)V", (void*)interopDatabaseAddNative},{"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative}};int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/AdapterService", sMethods,NELEM(sMethods));
}

然后注册好了,下面就可以调用了classInitNative

static void classInitNative(JNIEnv* env, jclass clazz) {jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic");android_bluetooth_UidTraffic.constructor =env->GetMethodID(jniUidTrafficClass, "<init>", "(IJJ)V");jclass jniCallbackClass =env->FindClass("com/android/bluetooth/btservice/JniCallbacks");sJniCallbacksField = env->GetFieldID(clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;");method_stateChangeCallback =env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");method_adapterPropertyChangedCallback = env->GetMethodID(jniCallbackClass, "adapterPropertyChangedCallback", "([I[[B)V");method_discoveryStateChangeCallback = env->GetMethodID(jniCallbackClass, "discoveryStateChangeCallback", "(I)V");method_devicePropertyChangedCallback = env->GetMethodID(jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V");method_deviceFoundCallback =env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");method_pinRequestCallback =env->GetMethodID(jniCallbackClass, "pinRequestCallback", "([B[BIZ)V");method_sspRequestCallback =env->GetMethodID(jniCallbackClass, "sspRequestCallback", "([B[BIII)V");method_bondStateChangeCallback =env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BI)V");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");method_energyInfo = env->GetMethodID(clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V");if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {ALOGE("No Bluetooth Library found");}
}

这里面主要是获取jave类中的方法,也及时为了jni cpp可以调用jave代码,给回调函数,第二个是加载蓝牙协议栈的库文件,#define DEFAULT_BT_LIBRARY_NAME "libbluetooth.so"

所以第一点就打通了jni cpp代码跟service java上下通信的途径!

然后我们再说第二点AdapterService onCreate方法调用

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

public void onCreate() {super.onCreate();debugLog("onCreate()");...initNative(isGuest(), isNiapMode(), isAtvDevice);...
}

因为前面我们已经jni跟jave的沟通渠道已经导通,所以initNative就是调用jni的函数

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,jboolean isNiapMode, jboolean isAtvDevice) {ALOGV("%s", __func__);...int ret = sBluetoothInterface->init(&sBluetoothCallbacks,isGuest == JNI_TRUE ? 1 : 0,isNiapMode == JNI_TRUE ? 1 : 0,isAtvDevice == JNI_TRUE ? 1 : 0);if (ret != BT_STATUS_SUCCESS && ret != BT_STATUS_DONE) {ALOGE("Error while setting the callbacks: %d\n", ret);sBluetoothInterface = NULL;return JNI_FALSE;}...return JNI_TRUE;
}

这里就进入到system bt中

另外,当binder通信成功后除了AdapterService静态方法调用以及onCreate方法调用,还有BluetoothManagerService.java中的onServiceConnected调用,这个不能忘记哈· 否则追不下去代码,另外,这些代码分析确实会感觉跳来跳去,还是结合流程图一起看!

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

public void onServiceConnected(ComponentName componentName, IBinder service) {String name = componentName.getClassName();if (DBG) {Slog.d(TAG, "BluetoothServiceConnection: " + name);}Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);if (name.equals("com.android.bluetooth.btservice.AdapterService")) {msg.arg1 = SERVICE_IBLUETOOTH;mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);} else if (name.equals("com.android.bluetooth.gatt.GattService")) {msg.arg1 = SERVICE_IBLUETOOTHGATT;} else {Slog.e(TAG, "Unknown service connected: " + name);return;}msg.obj = service;mHandler.sendMessage(msg);
}

会发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED消息出来,我们看下消息处理

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {if (DBG) {Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);}IBinder service = (IBinder) msg.obj;try {mBluetoothLock.writeLock().lock();if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {mBluetoothGatt =IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));continueFromBleOnState();break;} // else must be SERVICE_IBLUETOOTHmBinding = false;mTryBindOnBindTimeout = false;mBluetoothBinder = service;mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));if (!isNameAndAddressSet()) {Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);mHandler.sendMessage(getMsg);if (mGetNameAddressOnly) {return;}}...//Do enable requesttry {if (!mQuietEnable) {if (!mBluetooth.enable()) {Slog.e(TAG, "IBluetooth.enable() returned false");}} else {if (!mBluetooth.enableNoAutoConnect()) {Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");}}} catch (RemoteException e) {Slog.e(TAG, "Unable to call enable()", e);}} finally {mBluetoothLock.writeLock().unlock();}...break;
}

获取aidl接口,也就是调用到了这个类下面的enable

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

public synchronized boolean enable(boolean quietMode) {// Enforce the user restriction for disallowing Bluetooth if it was set.if (mUserManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM)) {debugLog("enable() called when Bluetooth was disallowed");return false;}Log.e(TAG, "AdapterService enable");debugLog("enable() - Enable called with quiet mode status =  " + quietMode);mQuietmode = quietMode;mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_ON);return true;
}

这个会触发status machine,会调用到OffState中processMessage

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public boolean processMessage(Message msg) {switch (msg.what) {case BLE_TURN_ON:transitionTo(mTurningBleOnState);break;default:infoLog("Unhandled message - " + messageString(msg.what));return false;}return true;
}

然后进入到TurningBleOnState类中的enter

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public void enter() {super.enter();sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY);mAdapterService.bringUpBle();
}
void bringUpBle() {...//Start Gatt servicesetProfileServiceState(GattService.class, BluetoothAdapter.STATE_ON);
}

开启gattservice服务

另外,ProfileService的状态改变会触发AdapterService的onProfileServiceStateChanged

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

public void onProfileServiceStateChanged(ProfileService profile, int state) {if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {throw new IllegalArgumentException(BluetoothAdapter.nameForState(state));}Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);m.obj = profile;m.arg1 = state;mHandler.sendMessage(m);
}

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

private void processProfileServiceStateChanged(ProfileService profile, int state) {switch (state) {case BluetoothAdapter.STATE_ON:if (!mRegisteredProfiles.contains(profile)) {Log.e(TAG, profile.getName() + " not registered (STATE_ON).");return;}if (mRunningProfiles.contains(profile)) {Log.e(TAG, profile.getName() + " already running.");return;}mRunningProfiles.add(profile);if (GattService.class.getSimpleName().equals(profile.getName())) {Log.e(TAG, "processProfileServiceStateChanged enableNative");// 这个地方就是调用libjni.so的方法,通过jni native调用enableNative();} else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length&& mRegisteredProfiles.size() == mRunningProfiles.size()) {mAdapterProperties.onBluetoothReady();updateUuids();setBluetoothClassFromConfig();initProfileServices();getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS);getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS_BLE);mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);}break;default:Log.e(TAG, "Unhandled profile state: " + state);}
}

然后这个动作会触发com_android_bluetooth_btservice_adapterService.cpp中的

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static jboolean enableNative(JNIEnv* env, jobject obj) {ALOGV("%s", __func__);if (!sBluetoothInterface) return JNI_FALSE;int ret = sBluetoothInterface->enable();return (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE: JNI_FALSE;
}

其中sBluetoothInterface 是hal接口,定义如下:

typedef struct {/** set to sizeof(bt_interface_t) */size_t size;/**
* Opens the interface and provides the callback routines
* to the implemenation of this interface.
* The |is_atv| flag indicates whether the local device is an Android TV
*/int (*init)(bt_callbacks_t* callbacks, bool is_atv);/** Enable Bluetooth. */int (*enable)(bool guest_mode);/** Disable Bluetooth. */int (*disable)(void);/** Closes the interface. */void (*cleanup)(void);/** Get all Bluetooth Adapter properties at init */int (*get_adapter_properties)(void);/** Get Bluetooth Adapter property of 'type' */int (*get_adapter_property)(bt_property_type_t type);/** Set Bluetooth Adapter property of 'type' *//* Based on the type, val shall be one of
* RawAddress or bt_bdname_t or bt_scanmode_t etc
*/int (*set_adapter_property)(const bt_property_t *property);/** Get all Remote Device properties */int (*get_remote_device_properties)(RawAddress *remote_addr);/** Get Remote Device property of 'type' */int (*get_remote_device_property)(RawAddress *remote_addr,bt_property_type_t type);/** Set Remote Device property of 'type' */int (*set_remote_device_property)(RawAddress *remote_addr,const bt_property_t *property);/** Get Remote Device's service record  for the given UUID */int (*get_remote_service_record)(const RawAddress& remote_addr,const bluetooth::Uuid& uuid);/** Start SDP to get remote services */int (*get_remote_services)(RawAddress *remote_addr);/** Start Discovery */int (*start_discovery)(void);/** Cancel Discovery */int (*cancel_discovery)(void);/** Create Bluetooth Bonding */int (*create_bond)(const RawAddress *bd_addr, int transport);/** Create Bluetooth Bond using out of band data */int (*create_bond_out_of_band)(const RawAddress *bd_addr, int transport,const bt_out_of_band_data_t *oob_data);/** Remove Bond */int (*remove_bond)(const RawAddress *bd_addr);/** Cancel Bond */int (*cancel_bond)(const RawAddress *bd_addr);/**
* Get the connection status for a given remote device.
* return value of 0 means the device is not connected,
* non-zero return status indicates an active connection.
*/int (*get_connection_state)(const RawAddress *bd_addr);/** BT Legacy PinKey Reply *//** If accept==FALSE, then pin_len and pin_code shall be 0x0 */int (*pin_reply)(const RawAddress *bd_addr, uint8_t accept,uint8_t pin_len, bt_pin_code_t *pin_code);/** BT SSP Reply - Just Works, Numeric Comparison and Passkey
* passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON &
* BT_SSP_VARIANT_CONSENT
* For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey
* shall be zero */int (*ssp_reply)(const RawAddress *bd_addr, bt_ssp_variant_t variant,uint8_t accept, uint32_t passkey);/** Get Bluetooth profile interface */const void* (*get_profile_interface) (const char *profile_id);/** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs *//* Configure DUT Mode - Use this mode to enter/exit DUT mode */int (*dut_mode_configure)(uint8_t enable);/* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len);/** BLE Test Mode APIs *//* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len);/** Sets the OS call-out functions that bluedroid needs for alarms and wake locks.
* This should be called immediately after a successful |init|.
*/int (*set_os_callouts)(bt_os_callouts_t *callouts);/** Read Energy info details - return value indicates BT_STATUS_SUCCESS or BT_STATUS_NOT_READY
* Success indicates that the VSC command was sent to controller
*/int (*read_energy_info)();/**
* Native support for dumpsys function
* Function is synchronous and |fd| is owned by caller.
* |arguments| are arguments which may affect the output, encoded as
* UTF-8 strings.
*/void (*dump)(int fd, const char **arguments);/**
* Clear /data/misc/bt_config.conf and erase all stored connections
*/int (*config_clear)(void);/**
* Clear (reset) the dynamic portion of the device interoperability database.
*/void (*interop_database_clear)(void);/**
* Add a new device interoperability workaround for a remote device whose
* first |len| bytes of the its device address match |addr|.
* NOTE: |feature| has to match an item defined in interop_feature_t (interop.h).
*/void (*interop_database_add)(uint16_t feature, const RawAddress *addr, size_t len);
} bt_interface_t;

这个enable是调用libbluetooth.so中的enable,也就是bluedroid bluetooth.cc或者android8之前的bluetooth.c

static int enable() {if (!interface_ready()) return BT_STATUS_NOT_READY;stack_manager_get_interface()->start_up_stack_async();return BT_STATUS_SUCCESS;
}

然后就是走bluedroid流程了

2. 上行方向

上行方向就是hidl service->system bt -> service -> framework -> Settings

协议层上传蓝牙协议栈初始化的callback如下:

/system/bt/btif/src/stack_manager.cc

static void event_signal_stack_up(UNUSED_ATTR void* context) {// Notify BTIF connect queue that we've brought up the stack. It's// now time to dispatch all the pending profile connect requests.btif_queue_connect_next();HAL_CBACK(bt_hal_cbacks, adapter_state_changed_cb, BT_STATE_ON);
}

这个就是system bt给bluetooth service jni发送蓝牙初始化成功的callback

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static void adapter_state_change_callback(bt_state_t status) {CallbackEnv sCallbackEnv(__func__);if (!sCallbackEnv.valid()) return;ALOGV("%s: Status is: %d", __func__, status);sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback,(jint)status);
}

因为我们前面已经打通了jni跟adapter service的通信通道,所以直接通过sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback,(jint)status) 就是调用了

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java 中的stateChangeCallback

void stateChangeCallback(int status) {if (status == AbstractionLayer.BT_STATE_OFF) {debugLog("stateChangeCallback: disableNative() completed");mAdapterStateMachine.sendMessage(AdapterState.STACK_DISABLED);} else if (status == AbstractionLayer.BT_STATE_ON) {String BT_SOC = getSocName();if (BT_SOC.equals("pronto")) {Log.i(TAG, "setting max audio connection to 2");mAdapterProperties.setMaxConnectedAudioDevices(2);}mAdapterStateMachine.sendMessage(AdapterState.BLE_STARTED);} else {Log.e(TAG, "Incorrect status " + status + " in stateChangeCallback");}
}

然后此部分到AdapterState中处理,由于目前是TurningBleOnState状态,所以入

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public boolean processMessage(Message msg) {switch (msg.what) {case BLE_STARTED:transitionTo(mBleOnState);break;...}return true;
}

状态转换,进入mBleOnState状态,所以会触发父类的BaseAdapterState 的enter函数

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public void enter() {int currState = getStateValue();infoLog("entered ");mAdapterService.updateAdapterState(mPrevState, currState);mPrevState = currState;
}

然后进入AdapterService中处理状态概念

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

void updateAdapterState(int prevState, int newState) {mAdapterProperties.setState(newState);if (mCallbacks != null) {int n = mCallbacks.beginBroadcast();debugLog("updateAdapterState() - Broadcasting state " + BluetoothAdapter.nameForState(newState) + " to " + n + " receivers.");for (int i = 0; i < n; i++) {try {mCallbacks.getBroadcastItem(i).onBluetoothStateChange(prevState, newState);} catch (RemoteException e) {debugLog("updateAdapterState() - Callback #" + i + " failed (" + e + ")");}}mCallbacks.finishBroadcast();}...
}

然后进入到framework层来处理

/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

public void onBluetoothStateChange(boolean on) {mCallback.onBluetoothStateChange(on);}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {Message msg =mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);mHandler.sendMessage(msg);
}

frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

case MESSAGE_BLUETOOTH_STATE_CHANGE: {int prevState = msg.arg1;int newState = msg.arg2;if (DBG) {Slog.d(TAG,"MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " + BluetoothAdapter.nameForState(newState));}mState = newState;bluetoothStateChangeHandler(prevState, newState);...break;
}

由于gatt服务没有起来,所以进入bluetoothStateChangeHandler需要先进行gatt service的bind

private void bluetoothStateChangeHandler(int prevState, int newState) {...Intent i = new Intent(IBluetoothGatt.class.getName());doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,UserHandle.CURRENT);...
}

这个同样进进入到bind成功,又进入到这个onServiceConnected流程,是不是很熟悉

public void onServiceConnected(ComponentName componentName, IBinder service) {String name = componentName.getClassName();if (DBG) {Slog.d(TAG, "BluetoothServiceConnection: " + name);}Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);if (name.equals("com.android.bluetooth.btservice.AdapterService")) {msg.arg1 = SERVICE_IBLUETOOTH;mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);} else if (name.equals("com.android.bluetooth.gatt.GattService")) {msg.arg1 = SERVICE_IBLUETOOTHGATT;} else {Slog.e(TAG, "Unknown service connected: " + name);return;}msg.obj = service;mHandler.sendMessage(msg);
}

发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED消息,然后进入到

frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java,调用continueFromBleOnState

case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {...IBinder service = (IBinder) msg.obj;try {mBluetoothLock.writeLock().lock();if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {mBluetoothGatt =IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));continueFromBleOnState();break;} // else must be SERVICE_IBLUETOOTH...
}

frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

在里面调用mBluetooth.onLeServiceUp();

private void continueFromBleOnState() {...else if (isBluetoothPersistedStateOnBluetooth() ||mEnableExternal) {// This triggers transition to STATE_ONmBluetooth.updateQuietModeStatus(mQuietEnable);mBluetooth.onLeServiceUp();persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);}...
}

通过aidl接口调用mBluetooth.onLeServiceUp(),然后从service回到了service部分

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

void onLeServiceUp() {mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_ON);
}

因为我们前面已经进入了BleOnState状态,所以在这里直接在BleOnState类中处理

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public boolean processMessage(Message msg) {switch (msg.what) {case USER_TURN_ON:mAdapterService.startBrEdrStartup();transitionTo(mTurningOnState);break;...}return true;
}

进入TurningOnState的状态后,调用enter,然后会调用startProfileServices函数

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public void enter() {super.enter();sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY);mAdapterService.startProfileServices();
}

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

void startProfileServices() {debugLog("startCoreServices()");Class[] supportedProfileServices = Config.getSupportedProfiles();if (supportedProfileServices.length == 1 && GattService.class.getSimpleName().equals(supportedProfileServices[0].getSimpleName())) {mAdapterProperties.onBluetoothReady();updateUuids();setBluetoothClassFromConfig();mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);} else {setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);}
}

然后又回到packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java处理

public boolean processMessage(Message msg) {switch (msg.what) {case BREDR_STARTED:transitionTo(mOnState);break;...}return true;
}

进入父类的enter处理mAdapterService.updateAdapterState(mPrevState, currState);

所以会触发父类的BaseAdapterState 的enter函数

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

public void enter() {int currState = getStateValue();infoLog("entered ");mAdapterService.updateAdapterState(mPrevState, currState);mPrevState = currState;
}

然后进入AdapterService中处理状态概念

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

void updateAdapterState(int prevState, int newState) {mAdapterProperties.setState(newState);if (mCallbacks != null) {int n = mCallbacks.beginBroadcast();debugLog("updateAdapterState() - Broadcasting state " + BluetoothAdapter.nameForState(newState) + " to " + n + " receivers.");for (int i = 0; i < n; i++) {try {mCallbacks.getBroadcastItem(i).onBluetoothStateChange(prevState, newState);} catch (RemoteException e) {debugLog("updateAdapterState() - Callback #" + i + " failed (" + e + ")");}}mCallbacks.finishBroadcast();}...
}

然后进入到framework层来处理

/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

public void onBluetoothStateChange(boolean on) {mCallback.onBluetoothStateChange(on);}

/frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {Message msg =mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);mHandler.sendMessage(msg);
}

frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

case MESSAGE_BLUETOOTH_STATE_CHANGE: {int prevState = msg.arg1;int newState = msg.arg2;if (DBG) {Slog.d(TAG,"MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " + BluetoothAdapter.nameForState(newState));}mState = newState;bluetoothStateChangeHandler(prevState, newState);...break;
}

然后调用bluetoothStateChangeHandler,这个函数中处理由于前面的服务已经起来,所以我们直接送广播上去

frameworks/base/services/core/java/com/android/server/BluetoothManagerService.java

private void bluetoothStateChangeHandler(int prevState, int newState) {...if (isStandardBroadcast) {if (prevState == BluetoothAdapter.STATE_BLE_ON) {// Show prevState of BLE_ON as OFF to standard usersprevState = BluetoothAdapter.STATE_OFF;}Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);}
}

那么广播送上去后,谁来接收呢?当然是Settings apk的BluetoothEnabler类

/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothEnabler.java

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// Broadcast receiver is always running on the UI thread here,// so we don't need consider thread synchronization.int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);handleStateChanged(state);}
};

然后就对应的把android的空间蓝牙开关打开了

/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothEnabler.java

void handleStateChanged(int state) {switch (state) {case BluetoothAdapter.STATE_TURNING_ON:mSwitchController.setEnabled(false);break;case BluetoothAdapter.STATE_ON:mSwitchController.setEnabled(true);setChecked(true);break;case BluetoothAdapter.STATE_TURNING_OFF:mSwitchController.setEnabled(false);break;case BluetoothAdapter.STATE_OFF:mSwitchController.setEnabled(true);setChecked(false);break;default:mSwitchController.setEnabled(true);setChecked(false);}
}

二. 流程图总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/477191.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL课程学习】:MySQL安装,MySQL如何登录和退出?MySQL的简单配置

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;MySQL课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 MySQL在Centos 7环境下的安装&#xff1a; 卸载…

安宝特分享 | 如何利用AR技术革新医疗实践:从远程急救到多学科协作

AR技术在国内外医院的应用 在现代医疗环境中&#xff0c;患者面临的挑战依然严峻&#xff1a;看病难、看病远、看病急。这些问题不仅影响了患者的治疗效果&#xff0c;也让医务工作者倍感压力。幸运的是&#xff0c;随着增强现实&#xff08;AR&#xff09;技术的发展&#xf…

macOS 无法安装第三方app,启用任何来源的方法

升级新版本 MacOS 后&#xff0c;安装下载的软件时&#xff0c;不能在 ”安全性与隐私” 中找不到 ”任何来源” 选项。 1. 允许展示任何来源 点击 启动器 (Launchpad) – 其他 (Other) – 终端 (Terminal)&#xff1a; 打开终端后&#xff0c;输入以下代码回车&#xff1a; …

Rust中Tracing 应用指南

欢迎来到这篇全面的Rust跟踪入门指南。Rust 的tracing是一个用于应用程序级别的诊断和调试的库。它提供了一种结构化的、异步感知的方式来记录日志和跟踪事件。与传统的日志记录相比&#xff0c;tracing能够更好地处理复杂的异步系统和分布式系统中的事件跟踪&#xff0c;帮助开…

介绍一下strset(arr,ch);(c基础)

hi , I am 36 适合对象c语言初学者 strset(arr,ch)函数 功能 是将arr数组全部赋值为ch 格式 #include<string.h> strset(arr,ch); 返回值为arr 链接分享一下arr的意义(c基础)(必看)(牢记)-CSDN博客 hi I am 36.thanks for your looking .&#x1f44d;&#x1…

Web3与智能合约:区块链技术下的数字信任体系

随着互联网的不断发展&#xff0c;Web3代表着我们迈入了一个去中心化、更加安全和智能的网络时代。作为Web3的核心组成部分&#xff0c;区块链技术为智能合约的出现和发展提供了强有力的基础。智能合约不仅仅是自动化的代码&#xff0c;它们正逐步成为重塑数字世界信任体系的关…

如何更改手机GPS定位

你是否曾想过更改手机GPS位置以保护隐私、玩游戏或访问受地理限制的内容&#xff1f;接下来我将向你展示如何使用 MagFone Location Changer 更改手机GPS 位置&#xff01;无论是在玩Pokmon GO游戏、发布社媒贴子&#xff0c;这种方法都快速、简单且有效。 第一步&#xff1a;下…

青少年编程等级考试C++一级,硬币反转问题

代码 #include<iostream>using namespace std;bool a[300];int main(){ int n,m; cin >> n >> m; for(int i 1;i < m;i) { for (int j 1;j < n;j) { if( j % i 0) { a[j] !a[j];…

微信小程序技术架构图

一、视图层1.WXML&#xff08;WeiXin Markup Language&#xff09; 这是微信小程序的标记语言&#xff0c;类似于 HTML。它用于构建小程序的页面结构。例如&#xff0c;通过标签来定义各种视图元素&#xff0c;如<view>&#xff08;类似于 HTML 中的<div>&#xff…

《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part2

资料来自李宏毅老师《生成式 AI》课程&#xff0c;如有侵权请通知下线 Introduction to Generative AI 2024 Spring 来源背景说明 该文档主要介绍了国立台湾大学&#xff08;NTU&#xff09;2024 年春季 “生成式人工智能&#xff08;GenAI&#xff09;” 课程的作业 5&#…

gocv调用opencv添加中文乱码的解决方案

前言 相信很多做视觉的同学在使用opencv给图片添加中文文字的时候会出现这样的乱码显示: 而实际上你期望的是“告警时间:2011-11-11 11:11:11 告警类型:脱岗检测告警 Area:XXXXX Camera:Camera001-001”这样的显示内容,那么这篇文章我将用很简单的方法来解决乱码问题,只需…

【自用】常用希腊字母表

常用希腊字母表 原文链接 https://xilazimu.net/

【UE5】在材质中计算模型在屏幕上的比例

ViewProperty节点有很多有意思的变量 例如用 ViewProperty 的 tan ⁡ ( FOV / 2 ) \tan(\text{FOV} / 2) tan(FOV/2) 输出&#xff0c;用它计算模型占屏幕的比例。 &#xff08;常用于for运算的次数优化&#xff0c;也可以用于各种美术效果&#xff09; ScaleOnScreen Obje…

耳朵“嗡嗡”响,这是怎么啦

耳朵嗡嗡响&#xff0c;医学上称为耳鸣&#xff08;Tinnitus&#xff09;&#xff0c;是一种常见的症状&#xff0c;表现为耳朵内持续或间歇性的嗡嗡声、响声或鸣叫声。耳鸣的原因有很多&#xff0c;以下是一些常见的原因和相应的解决方法&#xff1a; 常见原因 1. 长时间暴露…

力扣-位运算-1【算法学习day.41】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

科研深度学习:如何精选GPU以优化服务器性能

GPU在科研深度学习中的核心价值 在科研深度学习的范畴内&#xff0c;模型训练是核心环节。面对大规模参数的模型训练&#xff0c;这往往是科研过程中最具挑战性的部分。传统CPU的计算模式在处理复杂模型时&#xff0c;训练时间会随着模型复杂度的增加而急剧增长&#xff0c;这…

ssm面向品牌会员的在线商城小程序

摘要 随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。它将是直接管理面向品牌会员的在线商城小程序的最新形式。本小程序是以面向品牌会员的在线商城管理为目标&#xff0c;使用 java技术制…

客户流失分析综述

引言 客户流失这个术语通常用来描述在特定时间或合同期内停止与公司进行业务往来的客户倾向性[1]。传统上&#xff0c;关于客户流失的研究始于客户关系管理&#xff08;CRM&#xff09;[2]。在运营服务时&#xff0c;防止客户流失至关重要。过去&#xff0c;客户获取相对于流失…

大数据技术之SparkCore

RDD概述 什么是RDD RDD&#xff08;Resilient Distributed Dataset&#xff09;叫做弹性分布式数据集&#xff0c;是Spark中最基本的数据抽象。代码中是一个抽象类&#xff0c;它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。 RDD五大特性 RDD编程 RDD的创…

可视化建模与UML《协作图实验报告》

有些鸟儿毕竟是关不住的。 一、实验目的&#xff1a; 1、熟悉协作图的构件事物。 2、掌握协作图的绘制方法。 二、实验环境&#xff1a; window7 | 10 | 11 EA15 三、实验内容&#xff1a; 下面列出了打印文件时的工作流&#xff1a; 用户通过计算机指定要打印的文件。(2)打…