This is the sixth day of my participation in Gwen Challenge
Early flag is really not good to live ah, to go to work, but also to see the source code implementation, but fortunately finally put the application layer bluetooth operation process to figure out. In the future, I will not be confused when my test partners mention bugs
BluetoothManagerService startup process
SystemServer#startOtherService starts the BluetoothService service
private void startOtherServices(a) {...// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
// support Bluetooth - see bug 988521
if (isEmulator) {
Slog.i(TAG, "No Bluetooth Service (emulator)");
} else 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 {
traceBeginAndSlog("StartBluetoothService"); mSystemServiceManager.startService(BluetoothService.class); traceEnd(); }... }Copy the code
BluetoothService, BluetoothService added BluetoothManagerServicet to the system service, Other applications can be accessed through getSystemService.
class BluetoothService extends SystemService {
private BluetoothManagerService mBluetoothManagerService;
public BluetoothService(Context context) {
super(context);
mBluetoothManagerService = new BluetoothManagerService(context);
}
@Override
public void onStart(a) {}@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
mBluetoothManagerService);
} else if(phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mBluetoothManagerService.handleOnBootPhase(); }}@Override
public void onSwitchUser(int userHandle) {
mBluetoothManagerService.handleOnSwitchUser(userHandle);
}
@Override
public void onUnlockUser(int userHandle) { mBluetoothManagerService.handleOnUnlockUser(userHandle); }}Copy the code
The initialization of BluetoothAdapter
public static synchronized BluetoothAdapter getDefaultAdapter(a) {
if (sAdapter == null) {
// If BluetoothManagerService has been started then b is the BluetoothManagerService proxy object
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;
}
BluetoothAdapter(IBluetoothManager managerService) {
if (managerService == null) {
throw new IllegalArgumentException("bluetooth manager service is null");
}
try {
mServiceLock.writeLock().lock();
//
mService = managerService.registerAdapter(mManagerCallback);
} catch (RemoteException e) {
Log.e(TAG, "", e);
} finally {
mServiceLock.writeLock().unlock();
}
mManagerService = managerService;
mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
mToken = new Binder();
}
Copy the code
BluetoothManagerService#registerAdapter returns an AdapterServiceBinder object that holds an AdapterService, which can be understood as dealing with the underlying system, It also manages the profiles supported by the device. It is responsible for calling back the observer that responds when the corresponding profile connection state changes.
Bluetooth startup process
Bluetooth is turned on or off by calling the enable and disable of BluetoothAdapter
BluetoothAdapter
public boolean enable(a) {
if (isEnabled()) {
if (DBG) {
Log.d(TAG, "enable(): BT already enabled!");
}
return true;
}
try {
return mManagerService.enable(ActivityThread.currentPackageName());
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
return false;
}
Copy the code
MManagerService is a proxy object of BluetoothManagerService
public boolean enable(String packageName) throws RemoteException {
final int callingUid = Binder.getCallingUid();
final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
if (isBluetoothDisallowed()) {// Check whether Bluetooth is disabled
if (DBG) {
Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
}
return false;
}
if(! callerSystem) {// It is not a system call
if(! checkIfCallerIsForegroundUser()) {// Check whether the application is in the foreground. Non-foreground applications cannot be called
Slog.w(TAG, "enable(): not allowed for non-active and non system user");
return false;
}
BLUETOOTH_ADMIN_PERM check permissions
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
//startConsentUiIfNeeded logic is consistent with starting the activity ourselves through bluetoothAdapter. ACTION_REQUEST_ENABLE Action
if(! isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {return false; }}if (DBG) {
Slog.d(TAG, "enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = "
+ mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
}
synchronized (mReceiver) {
mQuietEnableExternal = false;
mEnableExternal = true;
// waive WRITE_SECURE_SETTINGS permission check
sendEnableMsg(false,
BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
}
if (DBG) {
Slog.d(TAG, "enable returning");
}
return true;
}
Copy the code
SendEnableMsg sent a MESSAGE_ENABLE message
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();
}
Copy the code
HandleEnable () is also called when MESSAGE_ENABLE is processed
private void handleEnable(boolean quietMode) {... 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); }... }Copy the code
At this point IBluetooth is bound and its implementation class is AdapterServiceBinder which holds AdapterService. MConnection BluetoothServiceConnection is an instance of the object, after the connection is successful a hairstyle MESSAGE_BLUETOOTH_SERVICE_CONNECTED news.
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);
}
public void onServiceDisconnected(ComponentName componentName) {
// Called if we unexpectedly disconnect.
String name = componentName.getClassName();
if (DBG) {
Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
}
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
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 disconnected: " + name);
return; } mHandler.sendMessage(msg); }}Copy the code
In the process of MESSAGE_BLUETOOTH_SERVICE_CONNECTED, a callback is registered to receive bluetooth status changes and the AdapterService#enable method is called to enable bluetooth.
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
...
// Get the AdapterServiceBinder agent objectmBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service)); .// Register bluetooth status change listenermBluetooth.registerCallback(mBluetoothCallback); .// Enable Bluetooth through the underlying service
mBluetooth.enable()
...
}
Copy the code
Related callback of mBluetoothCallback.
private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
@Override
public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState); mHandler.sendMessage(msg); }};Copy the code
In the process of handling MESSAGE_BLUETOOTH_STATE_CHANGE invokes a bluetoothStateChangeHandler will see a familiar here, registered radio listening bluetooth application layer change.
private void bluetoothStateChangeHandler(int prevState, int newState) {
boolean isStandardBroadcast = true;
if (prevState == newState) { // No change. Nothing to do.
return; }...if (isStandardBroadcast) {
if (prevState == BluetoothAdapter.STATE_BLE_ON) {
// Show prevState of BLE_ON as OFF to standard users
prevState = BluetoothAdapter.STATE_OFF;
}
// This is the action we registered to broadcast to listen for bluetooth status changes.
Intent intent = newIntent(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); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); }}Copy the code
The transmission process of Bluetooth lyrics
Related initialization of Avrcp
Avrcp#start() starts registering sessions to listen to MediaSessionManager
if(mMediaSessionManager ! =null) {
mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveSessionListener, null,
mHandler);
mMediaSessionManager.setCallback(mButtonDispatchCallback, null);
}
Copy the code
MediaSessionManager registration process:
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
if (sessionListener == null) {
throw new IllegalArgumentException("listener may not be null");
}
if (handler == null) {
handler = new Handler();
}
synchronized (mLock) {
if(mListeners.get(sessionListener) ! =null) {
Log.w(TAG, "Attempted to add session listener twice, ignoring.");
return;
}
SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener,
handler);
try {
mService.addSessionsListener(wrapper.mStub, notificationListener, userId);
mListeners.put(sessionListener, wrapper);
} catch (RemoteException e) {
Log.e(TAG, "Error in addOnActiveSessionsChangedListener.", e); }}}Copy the code
MService is an internal class of MediaSessionService, SessionManagerImpl, which is actually registered with the decorator as SessionsChangedWrapper
The mActiveSessionListener is implemented as follows,
private MediaSessionManager.OnActiveSessionsChangedListener mActiveSessionListener =
new MediaSessionManager.OnActiveSessionsChangedListener() {
@Override
public void onActiveSessionsChanged( List
newControllers)
{... addMediaPlayerController(controller); . }};Copy the code
AddMediaPlayerController is going to call update collector controller again
/* unregister to the old controller, update new IDs and register to the new controller */
private boolean updateCurrentController(int addrId, int browseId) {
boolean registerRsp = true;
updateNewIds(addrId, browseId);
MediaController newController = null;
MediaPlayerInfo info = getAddressedPlayerInfo();
if(info ! =null) {
newController = info.getMediaController();
}
if (DEBUG) {
Log.d(TAG, "updateCurrentController: " + mMediaController + " to " + newController);
}
synchronized (this) {
if (mMediaController == null| | (! mMediaController.equals(newController))) {if(mMediaController ! =null) {
mMediaController.unregisterCallback(mMediaControllerCb);
}
mMediaController = newController;
if(mMediaController ! =null) {
mMediaController.registerCallback(mMediaControllerCb, mHandler);
} else {
registerRsp = false;
}
}
}
updateCurrentMediaState();
return registerRsp;
}
Copy the code
Register mMediaControllerCb with MediaController. The implementation of mMediaControllerCb is as follows:
private class MediaControllerListener extends MediaController.Callback {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
if (DEBUG) {
Log.v(TAG, "onMetadataChanged");
}
updateCurrentMediaState();
}
@Override
public synchronized void onPlaybackStateChanged(PlaybackState state) {
if (DEBUG) {
Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
}
updateCurrentMediaState();
}
@Override
public void onSessionDestroyed(a) {
Log.v(TAG, "MediaController session destroyed");
synchronized (Avrcp.this) {
if(mMediaController ! =null) { removeMediaController(mMediaController.getWrappedInstance()); }}}@Override
public void onQueueChanged(List<MediaSession.QueueItem> queue) {
if (queue == null) {
Log.v(TAG, "onQueueChanged: received null queue");
return;
}
final AvrcpMessageHandler handler = mHandler;
if (handler == null) {
if (DEBUG) Log.d(TAG, "onQueueChanged: mHandler is already null");
return;
}
Log.v(TAG, "onQueueChanged: NowPlaying list changed, Queue Size = "+ queue.size()); handler.sendEmptyMessage(MSG_NOW_PLAYING_CHANGED_RSP); }}Copy the code
Initialization of MediaSession
The constructor
public MediaSession(@NonNull Context context, @NonNull String tag, int userId) {
if (context == null) {
throw new IllegalArgumentException("context cannot be null.");
}
if (TextUtils.isEmpty(tag)) {
throw new IllegalArgumentException("tag cannot be null or empty");
}
mMaxBitmapSize = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
mCbStub = new CallbackStub(this);
MediaSessionManager manager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
mBinder = manager.createSession(mCbStub, tag, userId);
mSessionToken = new Token(mBinder.getController());
mController = new MediaController(context, mSessionToken);
} catch (RemoteException e) {
throw new RuntimeException("Remote error creating session.", e); }}Copy the code
MediaSessionManager The process of creating mBinder objects
public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
@NonNull String tag, int userId) throws RemoteException {
return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
}
Copy the code
We only take mService as the remote agent for MediaSessionService#SessionManagerImpl
class SessionManagerImpl extends ISessionManager.Stub {...@Override
public ISession createSession(String packageName, ISessionCallback cb, String tag,
int userId) throws RemoteException {...return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
.getSessionBinder();
} finally{ Binder.restoreCallingIdentity(token); }}}Copy the code
CreateSessionInternal will eventually call createSessionLocked
/* * When a session is created the following things need to happen. * 1. Its callback binder needs a link to death * 2. It needs to be added to all sessions. * 3. It needs to be added to the priority stack. * 4. It needs to be added to the relevant user record. */
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
String callerPackageName, ISessionCallback cb, String tag) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.wtf(TAG, "Request from invalid user: " + userId);
throw new RuntimeException("Session request from invalid user.");
}
final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
callerPackageName, cb, tag, this, mHandler.getLooper());
try {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
user.mPriorityStack.addSession(session);
// Notice that it will place the newly created MediaSessionRecord
mHandler.postSessionsChanged(userId);
if (DEBUG) {
Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
}
return session;
}
Copy the code
MHandler. PostSessionsChanged (userId) final position is pushSessionsChanged execution
private void pushSessionsChanged(int userId) {
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
return;
}
List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
int size = records.size();
ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
for (int i = 0; i < size; i++) {
tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
}
pushRemoteVolumeUpdateLocked(userId);
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
SessionsListenerRecord record = mSessionsListeners.get(i);
if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
try {
// this jumps to Avrcp to add a listener via MediaController#registerCallback
record.mListener.onActiveSessionsChanged(tokens);
} catch (RemoteException e) {
Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
e);
mSessionsListeners.remove(i);
}
}
}
}
}
Copy the code
Sending of lyrics
Now the interaction process between the upper application and the system Bluetooth has basically been sorted out. Next, let’s look at the process of sending song information. MediaSession#setMetadata
public void setMetadata(@Nullable MediaMetadata metadata) {
if(metadata ! =null) {
metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
}
try {
//mBinder is the MediaSessionRecord internal class SessionStub
mBinder.setMetadata(metadata);
} catch (RemoteException e) {
Log.wtf(TAG, "Dead object in setPlaybackState.", e); }}Copy the code
//mBinder is the MediaSessionRecord internal class SessionStub
private final class SessionStub extends ISession.Stub {...@Override
public void setMetadata(MediaMetadata metadata) {... mHandler.post(MessageHandler.MSG_UPDATE_METADATA); }@Override
public void setPlaybackState(PlaybackState state) {
int oldState = mPlaybackState == null
? PlaybackState.STATE_NONE : mPlaybackState.getState();
int newState = state == null
? PlaybackState.STATE_NONE : state.getState();
synchronized (mLock) {
mPlaybackState = state;
}
final long token = Binder.clearCallingIdentity();
try {
mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState);
} finally{ Binder.restoreCallingIdentity(token); } mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); }... }Copy the code
You can see that a MessageHandler.MSG_UPDATE_METADATA message is sent via the SessionStub, and the final processing of the message is in pushMetadataUpdate
private void pushMetadataUpdate(a) {
synchronized (mLock) {
if (mDestroyed) {
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
// This is added in MediaController#registerCallback, and the registration process is in Avrcp. So the code jumps to avrcp.java
holder.mCallback.onMetadataChanged(mMetadata);
} catch (DeadObjectException e) {
logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
mControllerCallbackHolders.remove(i);
} catch (RemoteException e) {
logCallbackException("unexpected exception in pushMetadataUpdate", holder, e); }}}}Copy the code
MControllerCallbackHolders the element is added in MediaController# registerCallback, while MediaController# registerCallback in Avrcp) in Java is invoked.
private class MediaControllerListener extends MediaController.Callback {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
if (DEBUG) {
Log.v(TAG, "onMetadataChanged");
}
updateCurrentMediaState();
}
@Override
public synchronized void onPlaybackStateChanged(PlaybackState state) {
if (DEBUG) {
Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
}
updateCurrentMediaState();
}
@Override
public void onSessionDestroyed(a) {
Log.v(TAG, "MediaController session destroyed");
synchronized (Avrcp.this) {
if(mMediaController ! =null) { removeMediaController(mMediaController.getWrappedInstance()); }}}@Override
public void onQueueChanged(List<MediaSession.QueueItem> queue) {
if (queue == null) {
Log.v(TAG, "onQueueChanged: received null queue");
return;
}
final AvrcpMessageHandler handler = mHandler;
if (handler == null) {
if (DEBUG) Log.d(TAG, "onQueueChanged: mHandler is already null");
return;
}
Log.v(TAG, "onQueueChanged: NowPlaying list changed, Queue Size = "+ queue.size()); handler.sendEmptyMessage(MSG_NOW_PLAYING_CHANGED_RSP); }}Copy the code
At this point, the process of sending bluetooth lyrics and the process of bluetooth system are completed. Because the process is really complicated, to sum up:
- Avrcp registers the MediaSeeeion for listening activities,
- Whenever an active MedisSession is listened on, the MediaControllerListener object is placed into the corresponding MrdiaSessionRecord by the corresponding MediaController.
- When sending lyrics, the application app calls the system first, and then the system transfers to the Bluetooth application for information transmission.