ANR源码分析之Broadcast Timeout

 在上篇文章中,介绍了InputDispatcher Timeout的流程。本篇文章将介绍Broadcast Timeout的流程。 我们知道广播根据注册的方式的可以分为静态注册广播和动态注册广播,根据发送方式可以分为普通广播、有序广播、Sticky广播以及APP内广播。广播执行的方式有两种,一种是串行执行,另外一种是并发执行。并发执行的广播是不存在超时的情况,例如动态注册的非有序的广播。而串行执行的广播是有超时时间的,前台广播是10s,后台广播是60s。可以通过设置Intent.FLAG_RECEIVER_FOREGROUND标志位,将广播设置为前台广播,默认情况下,广播都是后台广播。静态注册的和有序的广播都是串行执行的。

 Broadcast Timeout的整体流程如下图所示:

《ANR源码分析之Broadcast Timeout》

1.sendBroadcast方法(ContextImpl.java)
 广播的发送是通过context.sendBroadcast()方法来实现的,Context类是一个抽象的类,具体的由ContextImpl类来实现。
 framework/core/java/android/app/ContextImpl.java

 /*
    * 发送广播
    */
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

2.broadcastIntent方法(ActivityManagerService.java)

   frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            //验证广播intent是否有效
            intent = verifyBroadcastLocked(intent);
           
            //获取调用者进程记录对象
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

 在broadcastIntent方法中有两个布尔值参数serialized和sticky来共同决定是普通广播,有序广播还是sticky广播,参数如下:

类型serializedsticky
sendBroadcastfalsefalse
sendOrderedBroadcasttruefalse
sendStickyBroadcastfalsetrue

3.broadcastIntentLocked方法(ActivityManagerService.java)

 final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);
       // 增加该标志,则广播不会发送到已停止的package
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);


        // 当没有启动完成,不允许启动新的进程
        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
        .............
        //获取userID
        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_NON_FULL, "broadcast", callerPackage);
        .............


        int[] users;
        if (userId == UserHandle.USER_ALL) {.
            users = mUserController.getStartedUserArrayLocked();//广播给所有已启动用户
        } else {.
            users = new int[] {userId};//广播给指定用户
        }


        List receivers = null;//记录着匹配当前intent的所有静态注册广播接收者;
        List<BroadcastFilter> registeredReceivers = null;//记录着匹配当前的所有动态注册的广播接收者
        //找出所有能接收该广播的receivers
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
           //根据intent查找相应的receivers
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }


        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                   // 查询动态注册的广播
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                // 查询动态注册的广播
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            }
        }


        //是否需要替换现已存在的广播
        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
        ......
        //开始处理并行广播
       //非有序广播,并且是动态注册的广播,则将广播记录添加到并行广播队列中,这样动态注册的广播将并行执行。
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            .......
            //根据Intent中的标志位FLAG_RECEIVER_FOREGROUND决定返回前台广播队列还是后台广播队列
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            //创建广播记录
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                    resultExtras, ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            //是否需要替换现有广播记录
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
                //将广播记录添加到并行广播队列中
                queue.enqueueParallelBroadcastLocked(r);
                //开始处理广播队列的广播
                queue.scheduleBroadcastsLocked();
            }
            //将动态注册的广播接收者设置为空
            registeredReceivers = null;
            NR = 0;
        }




        int ir = 0;
        ........
       
       // 处理串行广播
        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            //根据Intent中的标志位FLAG_RECEIVER_FOREGROUND决定返回前台广播队列还是后台广播队列
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId);
            ......
            //是否需要替换现已存在的广播
            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if (!replaced) {
                //将静态注册的广播添加到串行广播队列中
                queue.enqueueOrderedBroadcastLocked(r);
                //开始处理广播队列的广播
                queue.scheduleBroadcastsLocked();
            }
        } else {
            .......
        }


        return ActivityManager.BROADCAST_SUCCESS;//返回发送广播成功的标志
    }

    根据中Intent.FLAG_RECEIVER_FOREGROUND标志位决定是使用前台广播队列还是后台广播队列

    BroadcastQueue broadcastQueueForIntent(Intent intent) {
        final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
        ......
        return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
    }


    BroadcastQueue mFgBroadcastQueue;//前台广播队列
    BroadcastQueue mBgBroadcastQueue;//后台广播队列
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "background", BROADCAST_BG_TIMEOUT, true);
    static final int BROADCAST_FG_TIMEOUT = 10*1000;//前台广播超时时间为10s
    static final int BROADCAST_BG_TIMEOUT = 60*1000;//后台广播超时时间为60s

    前台广播队列和后台广播队列有两个不同对方,一个是超时时间,另外一个是allowDelayBehindServices参数,前台广播是false,表示不等待。后台广播是true,表示要等待。

  frameworks/services/core/java/com/android/server/am/BroadcastQueue.java

  public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
        mParallelBroadcasts.add(r);
        r.enqueueClockTime = System.currentTimeMillis();
    }
    /*
    * 并行广播队列,所有的广播都立即执行,不用等待其它广播执行完成。
    * 后台和前台广播是分开维护的。
    */
    final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
 public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
        mOrderedBroadcasts.add(r);
        r.enqueueClockTime = System.currentTimeMillis();
    }
    /*
    * 串行广播队列,在一个时间内只能执行一个广播,只能等待列表中前一个广播执行完成了,才能执行下       
    * 一个广播。
    * 前台广播和后台广播也是分开维护的。
    */
    final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();

4.scheduleBroadcastsLocked方法(BroadcastQueue.java)

public void scheduleBroadcastsLocked() {
        ......
        if (mBroadcastsScheduled) {
            return;
        }
        //发送BROADCAST_INTENT_MSG消息
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }


    public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
            }
    }

5.processNextBroadcast方法(BroadcastQueue.java)

final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;
            ......
            mService.updateCpuStats();//更新CPU的状态信息


            if (fromMsg) {
                mBroadcastsScheduled = false;//将mBroadcastsScheduled变量置为false,让后面的广播可以执行
            }


            // 1.首先处理并行广播
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                final int N = r.receivers.size();//获取广播接收者的数目
                .......
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    ........
                    //分发广播给已注册的receivers
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                // 添加广播到广播的历史统计中
                addBroadcastToHistoryLocked(r);
                .......
            }




            // 2.处理串行广播
           .......
            boolean looped = false;


            do {
                //所有的串行广播都执行完成了,则调度执行gc
                if (mOrderedBroadcasts.size() == 0) {
                    mService.scheduleAppGcsLocked();
                    if (looped) {
                        //执行完最后一个串行广播后,确保所有进程的OomAdj都是正确的
                        mService.updateOomAdjLocked();
                    }
                    return;
                }
                //获取串行广播队列中的第一个元素
                r = mOrderedBroadcasts.get(0);


                ......


                //没有更多的接收者来处理该广播了
                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {


                    ......
                    //取消广播超时
                    cancelBroadcastTimeoutLocked();


                   .........
                    //将广播从串行广播列表中移除
                    mOrderedBroadcasts.remove(0);
                    r = null;
                    looped = true;
                    continue;
                }
            } while (r == null);


            // 3.获取下条有序广播
            // 获取下一个接收者的index
            int recIdx = r.nextReceiver++;


            //跟踪记录receiver开始处理广播的时间,这样可以发送一个Timeout消息避免超时
            r.receiverTime = SystemClock.uptimeMillis();
            if (recIdx == 0) {
                r.dispatchTime = r.receiverTime;//记录首个接收执行该广播的开始时间
                r.dispatchClockTime = System.currentTimeMillis();
                ......
            }
            if (! mPendingBroadcastTimeoutMessage) {
                //广播超时时间
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                .......
                //设置广播超时时间,发送BROADCAST_TIMEOUT_MSG消息,这样超时后就产生ANR信息
                setBroadcastTimeoutLocked(timeoutTime);
            }


            final BroadcastOptions brOptions = r.options;
            // 获取下一个广播接收者
            final Object nextReceiver = r.receivers.get(recIdx);
            //对于动态注册的广播接收者,则交由deliverToRegisteredReceiverLocked函数处理
            if (nextReceiver instanceof BroadcastFilter) {


                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                ......
                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                if (r.receiver == null || !r.ordered) {
                    ........
                    r.state = BroadcastRecord.IDLE;
               //该广播已经处理完成了,接着处理下一个广播
                    scheduleBroadcastsLocked();
                } else {
                    ......
                }
                return;
            }


            // 对于静态注册的广播接收者
            ResolveInfo info =  (ResolveInfo)nextReceiver;
            ComponentName component = new ComponentName(
                    info.activityInfo.applicationInfo.packageName,
                    info.activityInfo.name);


            .......
            int perm = mService.checkComponentPermission(info.activityInfo.permission,
                    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
                    info.activityInfo.exported);


            ......


            final int receiverUid = info.activityInfo.applicationInfo.uid;
            String targetProcess = info.activityInfo.processName;
            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                    info.activityInfo.applicationInfo.uid, false);
             ..........
             //执行各种权限检测,此处省略,当权限不满足时skip=true
            //跳过执行该广播
            if (skip) {
                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
                r.receiver = null;
                r.curFilter = null;
                r.state = BroadcastRecord.IDLE;
                scheduleBroadcastsLocked();
                return;
            }


            .......
            r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
            r.state = BroadcastRecord.APP_RECEIVE;
            r.curComponent = component;
            r.curReceiver = info.activityInfo;
            .........
            // 4.处理下条有序广播
            // receiver是否已经启动运行了,如果已经启动了,则处理该广播
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    processCurBroadcastLocked(r, app);//处理当前广播
                    return;
                } catch (RemoteException e) {
                    .....
            }


            //如果receiver所对应的进程尚未启动,则创建该进程
            .....
            if ((r.curApp=mService.startProcessLocked(targetProcess,
                    info.activityInfo.applicationInfo, true,
                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                    "broadcast", r.curComponent,
                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                            == null) {
               
                //如果receiver启动失败,则结束该receiver
                ......
                logBroadcastReceiverDiscardLocked(r);
                finishReceiverLocked(r, r.resultCode, r.resultData,
                        r.resultExtras, r.resultAbort, false);
                scheduleBroadcastsLocked();
                r.state = BroadcastRecord.IDLE;
                return;
            }


            mPendingBroadcast = r;
            mPendingBroadcastRecvIndex = recIdx;
        }
    }

 从上面可以看到,processNextBroadcast方法首先处理并行广播,然后处理串行广播,并设置广播超时时间,最后获取下条串行广播,并处理串行广播。

6.setBroadcastTimeoutLocked方法(BroadcastQueue.java)

  /*
    * 设置广播超时时间
    */
    final void setBroadcastTimeoutLocked(long timeoutTime) {
        if (! mPendingBroadcastTimeoutMessage) {
            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
            //发送BROADCAST_TIMEOUT_MSG消息
            mHandler.sendMessageAtTime(msg, timeoutTime);
            mPendingBroadcastTimeoutMessage = true;
        }
    }
    public void handleMessage(Message msg) {
            switch (msg.what) {
                .....
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
                .......
            }
        }

7.broadcastTimeoutLocked方法(BroadcastQueue.java)

 final void broadcastTimeoutLocked(boolean fromMsg) {
        if (fromMsg) {
            mPendingBroadcastTimeoutMessage = false;
        }


        //如果有序广播列表为空,则停止发送ANR消息
        if (mOrderedBroadcasts.size() == 0) {
            return;
        }


        long now = SystemClock.uptimeMillis();
        //获取当前处理的广播
        BroadcastRecord r = mOrderedBroadcasts.get(0);
        if (fromMsg) {
            ......
            //如果应用进程还未启动,则直接返回,广播超时只对已经启动的应用有效
            if (!mService.mProcessesReady) {
                return;
            }


            //广播开始接收处理时间 + 超时时间(前台广播10s,后台广播60s)
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            //如果没有超时,则启动一下一个超时广播信息,超时时间没有改变
            if (timeoutTime > now) {
                .....
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }


        //下面是处理广播超时的
        BroadcastRecord br = mOrderedBroadcasts.get(0);
        .....
        //更新广播开始接收处理时间为当前时间
        r.receiverTime = now;
        r.anrCount++;
        .......
        ProcessRecord app = null;
        String anrMessage = null;


        Object curReceiver = r.receivers.get(r.nextReceiver-1);
        r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
        logBroadcastReceiverDiscardLocked(r);
        if (curReceiver instanceof BroadcastFilter) {
         ......
        } else {
            app = r.curApp;
        }


        if (app != null) {
            //获取超时的信息
            anrMessage = "Broadcast of " + r.intent.toString();
        }


        .......


        //下一个广播接收者处理
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        scheduleBroadcastsLocked();


        //将ANR信息发送到AppErrors中处理
        if (anrMessage != null) {
            mHandler.post(new AppNotResponding(app, anrMessage));
        }
    }

 在broadcastTimeoutLocked方法中,将投递ANR的信息投递到AppErrors中处理。  

 private final class AppNotResponding implements Runnable {
        private final ProcessRecord mApp;
        private final String mAnnotation;


        public AppNotResponding(ProcessRecord app, String annotation) {
            mApp = app;
            mAnnotation = annotation;
        }


        @Override
        public void run() {
            mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
        }
    }

 至此,Broadcast Timeout流程介绍完成了,可以看到在开始处理串行广播时,设置一个timeout超时时间,然后通过mHandler.sendMessageAtTime(msg, timeoutTime)方法,来控制发送ANR消息。如果串行广播执行没有超时的话,则会通过cancelBroadcastTimeoutLocked发起取消超时设置。cancelBroadcastTimeoutLocked方法的实现如下,具体是通过移除超时信息来实现。

final void cancelBroadcastTimeoutLocked() {
        if (mPendingBroadcastTimeoutMessage) {
            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
            mPendingBroadcastTimeoutMessage = false;
        }
    }
    原文作者:风再起时与不羁的风
    原文地址: https://blog.csdn.net/chewbee/article/details/72670603
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞