Source code analysis broadcast operation principle

Source code analysis broadcast operation principle

[[436974]]

Preface

There are various broadcasts in Android, such as battery usage status, phone call reception and SMS reception, which will generate a broadcast. Application developers can also monitor these broadcasts and process them according to program logic.

Today we will analyze the operating mechanism of broadcasting

1. Detailed explanation of broadcasting principle and mechanism

1. Analysis of static broadcast registration process

  • Static broadcast is registered by scanning installed applications through PackageManagerService at startup;
  • In the construction method of PackageManagerService, the application installation directory will be scanned, and the order is to scan the system application installation directory first and then the third-party application installation directory;

PackageManagerService.scanDirLI is the method used to scan directories:

  1. private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
  2. String[] files = dir.list();
  3. if (files == null ) {
  4. return ;
  5. }
  6. int i;
  7. for (i=0; i<files.length; i++) {
  8. File file = new File(dir, files[i]);
  9. if (!isPackageFilename(files[i])) {
  10. continue ;
  11. }
  12. PackageParser.Package pkg = scanPackageLI(file,
  13. flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null );
  14. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
  15. mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
  16. file.delete () ;
  17. }
  18. }
  19. }
  20. private static final boolean isPackageFilename(String name ) {
  21. return   name != null && name .endsWith( ".apk" );
  22. }
  • As you can see, it uses the File.list method to list all files with the suffix ".apk" in the directory and pass them to scanPackageLI for processing;
  • And scanPackageLI(File scanFile, int parseFlags, int scanMode, long currentTime, UserHandle user) will call its overloaded method internally;
  1. scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime, UserHandle user ):
  2. private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanMode, long currentTime, UserHandle user ) {
  3. ...
  4. final PackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags);
  5. ...
  6. PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user );
  7. ...
  8. }

In this scanPackageLIl, the Package will be parsed and the BroadcastReceiver registered in AndroidManifest.xml will be saved:

  1. ...
  2. N = pkg.receivers.size ();
  3. r = null ;
  4. for (i=0; i<N; i++) {
  5. PackageParser.Activity a = pkg.receivers.get(i);
  6. a.info.processName = fixProcessName(pkg.applicationInfo.processName,
  7. a.info.processName, pkg.applicationInfo.uid);
  8. mReceivers.addActivity(a, "receiver" );
  9. ...
  10. }
  11. ...

Static broadcast process:

  • The broadcast of system applications is registered before the broadcast of third-party applications;
  • The registration order of static broadcasts of applications installed in the same directory is based on the order of apks listed in File.list;
  • The order in which they are registered determines the order in which they receive broadcasts;
  • Through the static broadcast registration process, we have registered the static broadcast in mReceivers of PackageManagerService;

And we can use the PackageManagerService.queryIntentReceivers method to query the static broadcast corresponding to the intent

  1. public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {
  2. if (!sUserManager.exists(userId)) return Collections.emptyList();
  3. ComponentName comp = intent.getComponent();
  4. if (comp == null ) {
  5. if (intent.getSelector() != null ) {
  6. intent = intent.getSelector();
  7. comp = intent.getComponent();
  8. }
  9. }
  10. if (comp != null ) {
  11. List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
  12. ActivityInfo ai = getReceiverInfo(comp, flags, userId);
  13. if (ai != null ) {
  14. ResolveInfo ri = new ResolveInfo();
  15. ri.activityInfo = ai;
  16. list.add (ri);
  17. }
  18. return list;
  19. }
  20. synchronized (mPackages) {
  21. String pkgName = intent.getPackage();
  22. if (pkgName == null ) {
  23. return mReceivers.queryIntent(intent, resolvedType, flags, userId);
  24. }
  25. final PackageParser.Package pkg = mPackages.get(pkgName);
  26. if (pkg != null ) {
  27. return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
  28. userId);
  29. }
  30. return   null ;
  31. }
  32. }

2. Dynamic broadcast registration process analysis

When we call Context.registerReceiver, we will end up calling ActivityManagerService.registerReceiver:

  1. public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
  2. ...
  3. ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
  4. ...
  5. BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);
  6. ...
  7. mReceiverResolver.addFilter(bf);
  8. ...
  9. }

Therefore, you can get the dynamic broadcast corresponding to the intent through mReceiverResolver.queryIntent;

3. Analysis of the broadcast sending process

ActivityManagerNative.getDefault().broadcastIntent() is called in ContextImpl.sendBroadcast

  1. public void sendBroadcast(Intent intent) {
  2. warnIfCallingFromSystemProcess();
  3. String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
  4. try {
  5. intent.prepareToLeaveProcess();
  6. ActivityManagerNative.getDefault().broadcastIntent(
  7. mMainThread.getApplicationThread(), intent, resolvedType, null ,
  8. Activity.RESULT_OK, null , null , null , AppOpsManager.OP_NONE, false , false ,getUserId());
  9. } catch (RemoteException e) {
  10. }
  11. }

Actually, it calls ActivityManagerService.broadcastIntent:

  1. public final int broadcastIntent(IApplicationThread caller,
  2. Intent intent, String resolvedType, IIntentReceiver resultTo,
  3. int resultCode, String resultData, Bundle map,
  4. String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
  5. enforceNotIsolatedCaller( "broadcastIntent" );
  6. synchronized(this) {
  7. intent = verifyBroadcastLocked(intent);
  8. final ProcessRecord callerApp = getRecordForAppLocked(caller);
  9. final int callingPid = Binder.getCallingPid();
  10. final int callingUid = Binder.getCallingUid();
  11. final long origId = Binder.clearCallingIdentity();
  12. int res = broadcastIntentLocked(callerApp,
  13. callerApp != null ? callerApp.info.packageName : null ,
  14. intent, resolvedType, resultTo,
  15. resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
  16. callingPid, callingUid, userId);
  17. Binder.restoreCallingIdentity(origId);
  18. return res;
  19. }
  20. }

Call ActivityManagerService.broadcastIntentLocked, and the key code in broadcastIntentLocked is as follows:

  1. broadcastIntentLocked
  2. // Static broadcast
  3. List receivers = null ;
  4. // Dynamic broadcast
  5. List<BroadcastFilter> registeredReceivers = null ;
  6. if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
  7. == 0) {
  8. // Query static broadcast
  9. receivers = collectReceiverComponents(intent, resolvedType, users);
  10. }
  11. if (intent.getComponent() == null ) {
  12. // Query dynamic broadcast
  13. registeredReceivers = mReceiverResolver.queryIntent(intent,
  14. resolvedType, false , userId);
  15. }
  16. final boolean replacePending =
  17. (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
  18. int NR = registeredReceivers != null ? registeredReceivers. size () : 0;
  19. if (!ordered && NR > 0) {
  20. final BroadcastQueue queue = broadcastQueueForIntent(intent);
  21. BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
  22. callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
  23. appOp, registeredReceivers, resultTo, resultCode, resultData, map,
  24. ordered, sticky, false , userId);
  25. final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
  26. if (!replaced) {
  27. //Send dynamic broadcast
  28. queue.enqueueParallelBroadcastLocked(r);
  29. queue.scheduleBroadcastsLocked();
  30. }
  31. registeredReceivers = null ;
  32. NR = 0;
  33. }
  34. ...
  35. if ((receivers != null && receivers. size () > 0)
  36. || resultTo != null ) {
  37. BroadcastQueue queue = broadcastQueueForIntent(intent);
  38. BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
  39. callerPackage, callingPid, callingUid, resolvedType,
  40. requiredPermission, appOp, receivers, resultTo, resultCode,
  41. resultData, map, ordered, sticky, false , userId);
  42. boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
  43. if (!replaced) {
  44. //Send static broadcast
  45. queue.enqueueOrderedBroadcastLocked(r);
  46. queue.scheduleBroadcastsLocked();
  47. }
  48. }
  • Dynamic broadcasting takes precedence over static broadcasting, as we can see from the code above;
  • In fact, static broadcast static is queried from PackageManagerService:

  1. private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
  2. int [] users) {
  3. ...
  4. List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
  5. .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user );
  6. ...

4. Broadcast Queue

  • From ActivityManagerService.broadcastIntentLocked we can see that it does not actually send the broadcast directly to the BroadcastReceiver;
  • Instead, wrap it in a BroadcastRecord and put it in the BroadcastQueue:
  1. BroadcastQueue queue = broadcastQueueForIntent(intent);
  2. BroadcastRecord r = new BroadcastRecord(queue, intent, null ,
  3. null , -1, -1, null , null , AppOpsManager.OP_NONE, receivers, null , 0,
  4. null , null , false , true , true , -1);
  5. queue.enqueueParallelBroadcastLocked(r);
  6. queue.scheduleBroadcastsLocked();
  7. The enqueueParallelBroadcastLocked method is used to perform broadcasts concurrently. It is very simple, just put the BroadcastRecord in mParallelBroadcasts:
  8. public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
  9. mParallelBroadcasts.add (r) ;
  10. }

The scheduleBroadcastsLocked method is also very simple, it sends a BROADCAST_INTENT_MSG message to mHandler:

  1. public void scheduleBroadcastsLocked() {
  2. if (mBroadcastsScheduled) {
  3. return ;
  4. }
  5. mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
  6. mBroadcastsScheduled = true ;
  7. }

What does mHandler do when it receives a BROADCAST_INTENT_MSG message:

  1. final Handler mHandler = new Handler() {
  2. public void handleMessage(Message msg) {
  3. switch (msg.what) {
  4. case BROADCAST_INTENT_MSG: {
  5. processNextBroadcast( true );
  6. } break;
  7. case BROADCAST_TIMEOUT_MSG: {
  8. synchronized (mService) {
  9. broadcastTimeoutLocked( true );
  10. }
  11. } break;
  12. }
  13. }
  14. };

The processNextBroadcast method is used to obtain broadcast messages from the queue and send them to BroadcastReceiver. It has two branches: parallel processing and serial processing.

5. Parallel Processing

Dynamically registered non-ordered broadcasts use parallel processing:

  1. final void processNextBroadcast(boolean fromMsg) {
  2. synchronized(mService) {
  3. BroadcastRecord r;
  4. mService.updateCpuStats();
  5. if (fromMsg) {
  6. mBroadcastsScheduled = false ;
  7. }
  8. while (mParallelBroadcasts. size () > 0) {
  9. r = mParallelBroadcasts.remove(0);
  10. r.dispatchTime = SystemClock.uptimeMillis();
  11. r.dispatchClockTime = System.currentTimeMillis();
  12. final int N = r.receivers.size ();
  13. for ( int i=0; i<N; i++) {
  14. Object target = r.receivers.get(i);
  15. // Send a message to Receiver
  16. deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false );
  17. }
  18. addBroadcastToHistoryLocked(r);
  19. }
  20. ...
  21. }
  22. ...
  23. }
  24. private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
  25. BroadcastFilter filter, boolean ordered) {
  26. ...
  27. // Get the Binder of BroadcastReceiver
  28. r.receiver = filter.receiverList.receiver.asBinder();
  29. ...
  30. // Use the Binder mechanism to pass the message to BroadcastReceiver
  31. performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
  32. new Intent(r.intent), r.resultCode, r.resultData,
  33. r.resultExtras, r.ordered, r.initialSticky, r.userId);
  34. ...
  35. }
  36. void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
  37. Intent intent, int resultCode, String data, Bundle extras,
  38. boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
  39. ......
  40. //The message processing is passed to the application process through Binder, and the Handler mechanism is used inside the application process to put the message processing into the main thread
  41. app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
  42. data, extras, ordered, sticky, sendingUser, app.repProcState);
  43. ......
  44. }
  45. }

6. Serial processing

Ordered broadcast and static broadcast, etc., will be passed to BroadcastQueue through enqueueOrderedBroadcastLocked:

  1. public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
  2. mOrderedBroadcasts.add (r);
  3. }

Then in processNextBroadcast, mOrderedBroadcasts will be specially processed;

The principle of the broadcast queue transmitting the broadcast to the Receiver is actually to put the BroadcastReceiver and the message into the BroadcastRecord, and then traverse the BroadcastRecord in the BroadcastQueue through the Handler mechanism to send the message to the BroadcastReceiver;

2. Summary of Broadcasting Process

1. Android broadcasting is divided into two aspects: broadcast sender and broadcast receiver:

  • Broadcasting is a communication method between Android components. It can be used in the following scenarios:
  • Message communication within the same component in the same app (between single or multiple threads);
  • Message communication between different components within the same app (single process);
  • The same app has message communication between different components of multiple processes;
  • Message communication between components of different apps;
  • Message communication between the Android system and the App under certain circumstances;

2. Implementation principle

From the implementation principle point of view, broadcast in Android uses the observer mode, which is based on the publish/subscribe event model of messages. Therefore, from the implementation point of view, broadcast in Android decouples the sender and receiver of broadcast to a great extent, making the system easier to integrate and expand. The specific implementation process is roughly summarized as follows:

  • The broadcast receiver BroadcastReceiver registers with AMS (Activity Manager Service) through the Binder mechanism;
  • The broadcast sender sends broadcasts to AMS through the binder mechanism;
  • AMS searches for a BroadcastReceiver that meets the corresponding conditions (IntentFilter/Permission, etc.) and sends the broadcast to the corresponding message loop queue of the BroadcastReceiver;
  • After the message loop queue receives this broadcast, it calls back the onReceive() method in BroadcastReceiver;
  • The broadcast sender and broadcast receiver belong to the message publishing and subscription ends in the observer mode respectively, and AMS belongs to the processing center in the middle;
  • The execution of the broadcast sender and the broadcast receiver is asynchronous. The broadcast sent out does not care whether there is a receiver to receive it, nor does it determine when the receiver can receive it.

Summarize

Learning is like sailing against the current, if you don’t move forward, you will fall behind. The mind is like a horse running on a plain, it is easy to let go but hard to take back;

This article is reproduced from the WeChat public account "Android Development Programming"

<<:  Starting from March 1 next year, WeChat and Alipay personal payment codes will not be used for business payment collection. Media clarification

>>:  Alipay and WeChat payment codes are restricted. What will happen to individual merchants?

Recommend

Speculate on some trends in content marketing in 2017!

Some time ago, the Content Marketing Institute (C...

Research on audio and video playback technology in Android system

Author: Hu Rui, Unit: China Mobile Smart Home Ope...

The "Kraken" in Norse mythology, turns out to be this!

The "guest" we are interviewing today i...

Will artificial intelligence really destroy humanity?

What was originally a research project to maintai...

The feasibility of using a magnifying glass to focus moonlight to ignite fire!

For reprinting, business cooperation, scan the co...

Inventory of the latest Weibo advertising resources in 2018!

According to the recent "2018 China Mobile A...

How to build corporate brand awareness through the Internet?

Today is the Internet age. Everyone is using mobi...