Android -- Bootanimation是如何触发退出的

Android11开机动画退出流程分析 其中 EXIT_PROP_NAME为 service.bootanim.exit,当检测到该属性的值不为0时,就调用 requestExit 退出动画。在所有的windows都绘制完成之后,发送MSG_WINDOW_MANAGER_DRAWN_COMPLETE消息,最终调用到finishScreenTurningOn。进而调用到ActivityTaskManagerService的activityIdle函数中,调用栈如下。通过一步步的调用,最终调用到postFinishBooting,调用信息如下。 阅读详情

目录

Android -- Bootanimation是如何触发退出的


                    Android -- Bootanimation是如何触发退出的

ActivityManagerService启动的末尾阶段,会启动Launcher来作为系统的第一个App展示出来,主要调用在SystemServer中调用ActivityManagerService::systemReady():


    public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
        traceLog.traceBegin("PhaseActivityManagerReady");
        synchronized(this) {
            if (mSystemReady) {
                // If we're done calling all the receivers, run the next "boot phase" passed in
                // by the SystemServer
                if (goingCallback != null) {
                    goingCallback.run();
                }
                return;
            }

            mHasHeavyWeightFeature = mContext.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_CANT_SAVE_STATE);
            mLocalDeviceIdleController
                    = LocalServices.getService(DeviceIdleController.LocalService.class);
            mAssistUtils = new AssistUtils(mContext);
            mVrController.onSystemReady();
            // Make sure we have the current profile info, since it is needed for security checks.
            mUserController.onSystemReady();
            mRecentTasks.onSystemReadyLocked();
            mAppOpsService.systemReady();
            mSystemReady = true;
        }

        try {
            sTheRealBuildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
                    ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
                    .getSerial();
        } catch (RemoteException e) {}

        ArrayList<ProcessRecord> procsToKill = null;
        synchronized(mPidsSelfLocked) {
            for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
                ProcessRecord proc = mPidsSelfLocked.valueAt(i);
                if (!isAllowedWhileBooting(proc.info)){
                    if (procsToKill == null) {
                        procsToKill = new ArrayList<ProcessRecord>();
                    }
                    procsToKill.add(proc);
                }
            }
        }

        synchronized(this) {
            if (procsToKill != null) {
                for (int i=procsToKill.size()-1; i>=0; i--) {
                    ProcessRecord proc = procsToKill.get(i);
                    Slog.i(TAG, "Removing system update proc: " + proc);
                    removeProcessLocked(proc, true, false, "system update done");
                }
            }

            // Now that we have cleaned up any update processes, we
            // are ready to start launching real processes and know that
            // we won't trample on them any more.
            mProcessesReady = true;
        }

        Slog.i(TAG, "System now ready");
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY,
            SystemClock.uptimeMillis());

        synchronized(this) {
            // Make sure we have no pre-ready processes sitting around.

            if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                ResolveInfo ri = mContext.getPackageManager()
                        .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
                                STOCK_PM_FLAGS);
                CharSequence errorMsg = null;
                if (ri != null) {
                    ActivityInfo ai = ri.activityInfo;
                    ApplicationInfo app = ai.applicationInfo;
                    if ((app.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                        mTopAction = Intent.ACTION_FACTORY_TEST;
                        mTopData = null;
                        mTopComponent = new ComponentName(app.packageName,
                                ai.name);
                    } else {
                        errorMsg = mContext.getResources().getText(
                                com.android.internal.R.string.factorytest_not_system);
                    }
                } else {
                    errorMsg = mContext.getResources().getText(
                            com.android.internal.R.string.factorytest_no_action);
                }
                if (errorMsg != null) {
                    mTopAction = null;
                    mTopData = null;
                    mTopComponent = null;
                    Message msg = Message.obtain();
                    msg.what = SHOW_FACTORY_ERROR_UI_MSG;
                    msg.getData().putCharSequence("msg", errorMsg);
                    mUiHandler.sendMessage(msg);
                }
            }
        }

        retrieveSettings();
        final int currentUserId = mUserController.getCurrentUserId();
        synchronized (this) {
            readGrantedUriPermissionsLocked();
        }

        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
        if (pmi != null) {
            pmi.registerLowPowerModeObserver(ServiceType.FORCE_BACKGROUND_CHECK,
                    state -> updateForceBackgroundCheck(state.batterySaverEnabled));
            updateForceBackgroundCheck(
                    pmi.getLowPowerState(ServiceType.FORCE_BACKGROUND_CHECK).batterySaverEnabled);
        } else {
            Slog.wtf(TAG, "PowerManagerInternal not found.");
        }

        if (goingCallback != null) goingCallback.run();
        traceLog.traceBegin("ActivityManagerStartApps");
        mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                Integer.toString(currentUserId), currentUserId);
        mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
                Integer.toString(currentUserId), currentUserId);
        mSystemServiceManager.startUser(currentUserId);

        synchronized (this) {
            // Only start up encryption-aware persistent apps; once user is
            // unlocked we'll come back around and start unaware apps
            startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);

            // Start up initial activity.
            mBooting = true;
            // Enable home activity for system user, so that the system can always boot. We don't
            // do this when the system user is not setup since the setup wizard should be the one
            // to handle home activity in this case.
            if (UserManager.isSplitSystemUser() &&
                    Settings.Secure.getInt(mContext.getContentResolver(),
                         Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
                ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
                try {
                    AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
                            UserHandle.USER_SYSTEM);
                } catch (RemoteException e) {
                    throw e.rethrowAsRuntimeException();
                }
            }
            startHomeActivityLocked(currentUserId, "systemReady");//启动Launcher

            try {
                if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
                    Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
                            + " data partition or your device will be unstable.");
                    mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
                }
            } catch (RemoteException e) {
            }

            if (!Build.isBuildConsistent()) {
                Slog.e(TAG, "Build fingerprint is not consistent, warning user");
                mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
            }

            long ident = Binder.clearCallingIdentity();
            try {
                Intent intent = new Intent(Intent.ACTION_USER_STARTED);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND);
                intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
                broadcastIntentLocked(null, null, intent,
                        null, null, 0, null, null, null, OP_NONE,
                        null, false, false, MY_PID, SYSTEM_UID,
                        currentUserId);
                intent = new Intent(Intent.ACTION_USER_STARTING);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
                broadcastIntentLocked(null, null, intent,
                        null, new IIntentReceiver.Stub() {
                            @Override
                            public void performReceive(Intent intent, int resultCode, String data,
                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                                    throws RemoteException {
                            }
                        }, 0, null, null,
                        new String[] {INTERACT_ACROSS_USERS}, OP_NONE,
                        null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
            } catch (Throwable t) {
                Slog.wtf(TAG, "Failed sending first user broadcasts", t);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
            mStackSupervisor.resumeFocusedStackTopActivityLocked();
            mUserController.sendUserSwitchBroadcasts(-1, currentUserId);

            BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
            BinderInternal.nSetBinderProxyCountEnabled(true);
            BinderInternal.setBinderProxyCountCallback(
                    new BinderInternal.BinderProxyLimitListener() {
                        @Override
                        public void onLimitReached(int uid) {
                            Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                                    + Process.myUid());
                            if (uid == Process.SYSTEM_UID) {
                                Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
                            } else {
                                killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
                                        "Too many Binders sent to SYSTEM");
                            }
                        }
                    }, mHandler);

            traceLog.traceEnd(); // ActivityManagerStartApps
            traceLog.traceEnd(); // PhaseActivityManagerReady
        }
    }
startHomeActivityLocked(currentUserId, "systemReady");//启动Launcher

Launcher也是一个标准的Android App,启动流程与其他普通App一样,这里面不再去看它具体的处理了。每个App启动之后,都会在它的UI thread中(ActivityThread对象)执行Activity的生命周期函数,页面显示出来之后,会调用onResume()函数,对应到ActivityThread的flow就是:


    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }

        final Activity a = r.activity;

        if (localLOGV) {
            Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
                    + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
        }

        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        // Get rid of anything left hanging around.
        cleanUpPendingRemoveWindows(r, false /* force */);

        // The window is now visible if it has been added, we are not
        // simply finishing, and we are not starting another activity.
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            if (r.newConfig != null) {
                performConfigurationChangedForActivity(r, r.newConfig);
                if (DEBUG_CONFIGURATION) {
                    Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
                            + r.activity.mCurrentConfig);
                }
                r.newConfig = null;
            }
            if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
            WindowManager.LayoutParams l = r.window.getAttributes();
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                    != forwardBit) {
                l.softInputMode = (l.softInputMode
                        & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                        | forwardBit;
                if (r.activity.mVisibleFromClient) {
                    ViewManager wm = a.getWindowManager();
                    View decor = r.window.getDecorView();
                    wm.updateViewLayout(decor, l);
                }
            }

            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }

        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }

在执行handleResumeActivity()最后阶段,会向当前的Looper::MessageQueue注册一个IdlerHandler,以便在没有msg需要处理的闲暇时刻,做一些其他处理:

   /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

......


    private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            boolean stopProfiling = false;
            if (mBoundApplication != null && mProfiler.profileFd != null
                    && mProfiler.autoStopProfiler) {
                stopProfiling = true;
            }
            if (a != null) {
                mNewActivities = null;
                IActivityManager am = ActivityManager.getService();
                ActivityClientRecord prev;
                do {
                    if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    if (a.activity != null && !a.activity.mFinished) {
                        try {
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);//这里会执行activityIdle()
                            a.createdConfig = null;
                        } catch (RemoteException ex) {
                            throw ex.rethrowFromSystemServer();
                        }
                    }
                    prev = a;
                    a = a.nextIdle;
                    prev.nextIdle = null;
                } while (a != null);
            }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            ensureJitEnabled();
            return false;
        }
    }

这里主要是调用AMS::activityIdle():

    @Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        synchronized (this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                ActivityRecord r =
                        mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
                                false /* processPausingActivities */, config);
                if (stopProfiling) {
                    if ((mProfileProc == r.app) && mProfilerInfo != null) {
                        clearProfilerLocked();
                    }
                }
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

继续调用ActivityStackSupervisor::activityIdleInternalLocked():


    // Checked.
    @GuardedBy("mService")
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);

        ArrayList<ActivityRecord> finishes = null;
        ArrayList<UserState> startingUsers = null;
        int NS = 0;
        int NF = 0;
        boolean booting = false;
        boolean activityRemoved = false;

        ActivityRecord r = ActivityRecord.forTokenLocked(token);
        if (r != null) {
            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
                    + Debug.getCallers(4));
            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
            r.finishLaunchTickingLocked();
            if (fromTimeout) {
                reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
            }

            // This is a hack to semi-deal with a race condition
            // in the client where it can be constructed with a
            // newer configuration from when we asked it to launch.
            // We'll update with whatever configuration it now says
            // it used to launch.
            if (config != null) {
                r.setLastReportedGlobalConfiguration(config);
            }

            // We are now idle.  If someone is waiting for a thumbnail from
            // us, we can now deliver.
            r.idle = true;

            //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
            if (isFocusedStack(r.getStack()) || fromTimeout) {
                booting = checkFinishBootingLocked();//这里检测是否开机完成了
            }
        }

        if (allResumedActivitiesIdle()) {
            if (r != null) {
                mService.scheduleAppGcsLocked();
            }

            if (mLaunchingActivity.isHeld()) {
                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
                if (VALIDATE_WAKE_LOCK_CALLER &&
                        Binder.getCallingUid() != Process.myUid()) {
                    throw new IllegalStateException("Calling must be system uid");
                }
                mLaunchingActivity.release();
            }
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }

        // Atomically retrieve all of the other things to do.
        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
                true /* remove */, processPausingActivities);
        NS = stops != null ? stops.size() : 0;
        if ((NF = mFinishingActivities.size()) > 0) {
            finishes = new ArrayList<>(mFinishingActivities);
            mFinishingActivities.clear();
        }

        if (mStartingUsers.size() > 0) {
            startingUsers = new ArrayList<>(mStartingUsers);
            mStartingUsers.clear();
        }

        // Stop any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NS; i++) {
            r = stops.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
                if (r.finishing) {
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
                            "activityIdleInternalLocked");
                } else {
                    stack.stopActivityLocked(r);
                }
            }
        }

        // Finish any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NF; i++) {
            r = finishes.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
            }
        }

        if (!booting) {
            // Complete user switch
            if (startingUsers != null) {
                for (int i = 0; i < startingUsers.size(); i++) {
                    mService.mUserController.finishUserSwitch(startingUsers.get(i));
                }
            }
        }

        mService.trimApplications();
        //dump();
        //mWindowManager.dump();

        if (activityRemoved) {
            resumeFocusedStackTopActivityLocked();
        }

        return r;
    }

主要flow是:


    /**
     * Called when the frontmost task is idle.
     * @return the state of mService.mBooting before this was called.
     */
    @GuardedBy("mService")
    private boolean checkFinishBootingLocked() {
        final boolean booting = mService.mBooting;
        boolean enableScreen = false;
        mService.mBooting = false;
        if (!mService.mBooted) {
            mService.mBooted = true;
            enableScreen = true;
        }
        if (booting || enableScreen) {
            mService.postFinishBooting(booting, enableScreen);
        }
        return booting;
    }

因为这里还处于开机阶段,所以mService::mBooted为false / mService::mBooting为true,所以我们会继续执行postFinishBooting();但开机完成后,这些flag就会被reset、从而让这个流程只会执行一次:

    void AMS::postFinishBooting(boolean finishBooting, boolean enableScreen) {
        mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
                finishBooting ? 1 : 0, enableScreen ? 1 : 0));
    }

发送FINISH_BOOTING_MSG msg去完成boot工作,msg在AMS::MainHandler中处理:

            case FINISH_BOOTING_MSG: {
                if (msg.arg1 != 0) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
                    finishBooting();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                }
                if (msg.arg2 != 0) {
                    enableScreenAfterBoot();
                }
                break;
            }

根据参数,finishBooting() / enableScreenAfterBoot()都会执行,但退出开机动画只与enableScreenAfterBoot():

    void enableScreenAfterBoot() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                SystemClock.uptimeMillis());
        mWindowManager.enableScreenAfterBoot();//WMS::enableScreenAfterBoot()

        synchronized (this) {
            updateEventDispatchingLocked();
        }
    }

执行WMS::enableScreenAfterBoot():

    public void enableScreenAfterBoot() {
        synchronized(mWindowMap) {
            if (DEBUG_BOOT) {
                RuntimeException here = new RuntimeException("here");
                here.fillInStackTrace();
                Slog.i(TAG_WM, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
                        + " mForceDisplayEnabled=" + mForceDisplayEnabled
                        + " mShowingBootMessages=" + mShowingBootMessages
                        + " mSystemBooted=" + mSystemBooted, here);
            }
            if (mSystemBooted) {
                return;
            }
            mSystemBooted = true;
            hideBootMessagesLocked();
            // If the screen still doesn't come up after 30 seconds, give
            // up and turn it on.
            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
        }

        mPolicy.systemBooted();

        performEnableScreen();//执行这里;
    }

......

    private void performEnableScreen() {
        synchronized(mWindowMap) {
            if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
                    + " mForceDisplayEnabled=" + mForceDisplayEnabled
                    + " mShowingBootMessages=" + mShowingBootMessages
                    + " mSystemBooted=" + mSystemBooted
                    + " mOnlyCore=" + mOnlyCore,
                    new RuntimeException("here").fillInStackTrace());
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted && !mShowingBootMessages) {
                return;
            }

            if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
                return;
            }

            // Don't enable the screen until all existing windows have been drawn.
            if (!mForceDisplayEnabled
                    // TODO(multidisplay): Expand to all displays?
                    && getDefaultDisplayContentLocked().checkWaitingForWindows()) {
                return;
            }

            if (!mBootAnimationStopped) {
                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
                // stop boot animation
                // formerly we would just kill the process, but we now ask it to exit so it
                // can choose where to stop the animation.
                SystemProperties.set("service.bootanim.exit", "1");
                mBootAnimationStopped = true;
            }

            if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
                if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
                return;
            }

            try {
                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                if (surfaceFlinger != null) {
                    Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                    Parcel data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                            data, null, 0);
                    data.recycle();
                }
            } catch (RemoteException ex) {
                Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
            }

            EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
            mDisplayEnabled = true;
            if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");

            // Enable input dispatch.
            mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
        }

        try {
            mActivityManager.bootAnimationComplete();
        } catch (RemoteException e) {
        }

        mPolicy.enableScreenAfterBoot();

        // Make sure the last requested orientation has been applied.
        updateRotationUnchecked(false, false);
    }

通过向SurfaceFlinger发送FIRST_CALL_TRANSACTION,来触发开机动画退出,Native层定义在ISurfaceComposer.h里:

class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
    enum {
        // Note: BOOT_FINISHED must remain this value, it is called from
        // Java by ActivityManagerService.
        BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
        CREATE_CONNECTION,
        UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
    ........ 
    }  
.......
}

根据TRANSACTION ID,查看对应的function,ISurfaceComposer.cpp中:

        case BOOT_FINISHED: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            bootFinished();
            return NO_ERROR;
        }

SurfaceFlinger是BnSurfaceComposer的实现部分,所以bootFinished()的定义为:


void SurfaceFlinger::bootFinished()
{
    if (mStartPropertySetThread->join() != NO_ERROR) {
        ALOGE("Join StartPropertySetThread failed!");
    }
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );

    // wait patiently for the window manager death
    const String16 name("window");
    sp<IBinder> window(defaultServiceManager()->getService(name));
    if (window != 0) {
        window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
    }

    if (mVrFlinger) {
      mVrFlinger->OnBootFinished();
    }

    // stop boot animation
    // formerly we would just kill the process, but we now ask it to exit so it
    // can choose where to stop the animation.
    property_set("service.bootanim.exit", "1");

    const int LOGTAG_SF_STOP_BOOTANIM = 60110;
    LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                   ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));

    sp<LambdaMessage> readProperties = new LambdaMessage([&]() {
        readPersistentProperties();
    });
    postMessageAsync(readProperties);
}

设置退出属性service.bootanim.exit为1,这样bootanimation在checkExit()的时候,判断到属性已经被设置为1,就会退出bootanimation进程了,开机动画结束、show出Launcher界面

 

 

 

Android开机动画启动与结束机制深度解析 本文深度解析Android开机动画的启动与结束机制。开机动画由SurfaceFlinger服务初始化后触发启动,由独立的bootanimation进程负责解析和渲染bootanimation.zip动画文件。动画的结束则由系统在Launcher准备就绪后,通过设置service.bootanim.exit属性信号来触发停止流程。文章还提供了自定义动画和监听结束事件的实战方法。 阅读详情

相关推荐

rk3562 android 14 延长开机动画规避fallbackhome黑屏

本文介绍了在Android设备上去除开机加载过程(fallbackhome)的修改方法。针对非手机平板类设备,通过修改ResolverActivity.java、PackageManagerService.java和FallbackHome.java文件,实现跳过原生引导界面直接进入指定启动页面。关键修改包括:设置默认启动器包名和类名、在PackageManagerService中添加包名检查、以及注释掉FallbackHome中的进度显示代码。同时需要注意补充引导完成的属性设置,否则可能导致开发者模式或U

LaoK__的博客 782

Android SystemServer BpBinder监控源码分析

如果有Java层,BpBinder监控也可以理解为BinderProxy的监控。

大模型训练 1241

安卓开机动画BootAnimation启动源码分析

学习版本:AOSP 8.1.0_r1(对应Android 8.1系统)bootanimation frameworks/base/cmds/bootanimation/ surfaceflinger frameworks/native/services/surfaceflinger/ init system/core/init/ 该脚本是 Android 系统底层图形渲染核心服务 SurfaceFlinger的 init 启动配置,由系统 1 号 init 进程解析执行。 服务以

Beforthing的博客 448

应用对系统的跨进程调用次数太多,超过了系统规定的阈值,导致应用被Kill

1.问题描述: 【Super Cleaner-Antivirus..】【必现】【非单机问题】点击进入软件,软件闪退 2.根本原因 从log来看,是由于应用发送了太多Binder给系统,也就是应用对系统的跨进程调用次数太多,超过了系统规定的阈值,导致应用被Kill。 05-29 12:04:32.364 1218 1374 E ActivityManager: Uid 10109 sent too many Binders to uid 1000 05-29 12:04:32.365 1218 1374

hanhan1016的专栏 5037

Android 12开机动画定制实战:bootanimation解析与故障排查

从Android系统启动流程中的开机动画谈起,bootanimation作为C++实现的可执行程序,由init进程拉起,并通过SurfaceFlinger的vsync机制驱动渲染。在Android 12中,渲染链路全面接入BufferQueue与SurfaceControl,动画的帧率、退出逻辑和系统属性紧密关联。理解desc.txt的语法规则、zip包的存储格式以及SELinux上下文,是定制稳定动画包的前提。本文结合AOSP源码和实际调试经验,剖析了bootanimation从启动到退出的完整链路,并针

weixin_29256771的博客 125

安卓7.0 开机动画和launcher之间的黑屏...如何解决?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值。

**My Coding Family** 611

Android开机速度优化

问题描述 开机时间相对参考机过慢,大约慢15s左右。Android 系统7.0。 问题分析 开机问题涉及的层次较多,大致有bootloader-->kernel-->Zygote-->PMS-->AMS-->Launcher 可以借助bootchart来分析,也可以直接通过log分析。不幸的是本项目机器因未知原因导致无法抓取到bootchart。 幸好在我浏览源码时发现了一个神器perfboot工具。具体在system/core/init/perfboot.py。 运行该

Venus 的博客 7983

开机黑屏、动画提前退出根治方案:BootAnimation 底层流程 + 定制全教程

本文深入解析Android开机动画(BootAnimation)的底层机制与开发实践。首先梳理Android七层启动时序,明确BootAnimation在SurfaceFlinger之后、Zygote之前启动的定位,揭示其掩盖系统加载黑屏的核心作用。文章详细拆解AOSP源码中的关键实现:init通过bootanim.rc配置管理动画服务;SurfaceFlinger通过系统属性控制启停;纯Native的BootAnimation进程实现双缓冲渲染逻辑。

郑伟强dev的专栏 203

BootAnimation 开机动画一直播放, 直到在launcher中让它停止

最近做个项目, android10, 开机动画完成后, 还要经过FallbackHome的wallpaper界面, 然后还要黑一下才能显示launcher界面, launcher界面加载的又比较久,客户希望像以前那样, 动画过后直接显示launcher界面 \frameworks\base\cmds\bootanimation\BootAnimation.cpp 在检查动画退出的地方, 加一个自己的标志 然后在想要停止动画的地方将该标志置为1即可 ...

liwentang12的专栏 773

BootAnimation+SE+开机MP4动画播放

本文记录了Android系统开机动画(bootanimation)的相关调试过程。主要内容包括:1)编译bootanimation模块;2)验证视频编码器可用性,通过adb推送并播放测试视频;3)修改系统属性service.bootanim.exit控制动画退出;4)手动启动bootanimation可执行文件并观察日志输出;5)获取系统属性状态;6)检查生成的libbootanimation.so文件时间戳。调试过程涉及adb命令、属性设置、日志分析等多种技术手段,为Android开机动画的定制和调试提供

Android小码家 336

EFR32BG22 Thunderboard开发板 使用SPI 读取加速度计

Simplicity Studio v5 创建Silicon Labs蓝牙SoC EFR32BG22 Thunderboard模块的加速度计工程,模块上有一个ICM-20648 6轴运动跟踪设备,集成3轴加速度计和3轴陀螺仪功能,使用SPI接口跟EFR32BG22芯片连接。 我尽量以图片的形式展示(少打字): 将EFR32BG22的Thunderboard模块通过USB线插入到电脑,打开Simplicity Studio v5软件。 下面看重点: 点击搜索到的IO Stream: USART,点击Ins

qq_25727979的博客 1494

Android Framework源码解析(七):BootAnimation 启动流程解析——开机动画是如何显示出来的?

上一篇我们完整梳理了 Launcher3 桌面启动全流程,理解系统如何拉起桌面应用并渲染 UI。

gxgldyh的专栏 280

BRep实战指南:从理论到文件格式的全面解析

本文全面解析BRep(边界表示法)从基础概念到文件格式的实战应用。详细介绍了BRep的数学定义、工业应用场景,以及STEP、IGES等主流文件格式的对比与使用技巧,帮助读者掌握三维建模核心技术并优化性能。

weixin_29235923的博客 407

Android Framework开机动画开发

开机动画基本流程

abcdefg1289的博客 818

VXLAN技术详解

VXLAN(虚拟可扩展局域网)是一种Overlay二层虚拟化技术,通过MAC-in-UDP封装在IP网络上构建大规模虚拟二层网络。核心组件包括VTEP(隧道端点)、24位VNI(支持1600万个虚拟网络)和VXLAN网关。相比VLAN,VXLAN具有超大规模扩展、跨三层互联、支持虚拟机无缝迁移等优势,适用于云数据中心多租户隔离、跨数据中心互联等场景。其基于IP路由的封装方式无需改造物理网络,为现代云网络提供了弹性、安全的虚拟化解决方案。

kjkdd的博客 595

手把手教你用ADS仿真FMMT417雪崩三极管,搞定射频脉冲源设计

本文详细介绍了如何使用ADS仿真工具设计基于FMMT417雪崩三极管的射频脉冲源。从器件选型、SPICE模型导入到电路设计与优化,提供了一套完整的实操方案,特别适合射频脉冲设计的初学者。通过级联设计和实测验证,帮助工程师快速掌握雪崩电路的应用技巧,提升射频脉冲源的设计效率。

weixin_30905981的博客 489

Android开机动画背后的秘密:从SurfaceFlinger到ActivityManager的完整流程解析

本文深入解析了Android开机动画的完整生命周期,从SurfaceFlinger启动动画进程,到BootAnimation渲染循环,最终由ActivityManager的空闲通知触发关闭流程。文章详细阐述了各核心系统服务间的协同机制,并提供了监听动画结束的实用技巧与调试方法,是理解Android系统启动链条的绝佳指南。

cake8的博客 189

❤️爆肝十二万字《python从零到精通教程》,从零教你变大佬❤️(建议收藏)

文章目录强烈推荐系列教程,建议学起来!!一.pycharm下载安装二.python下载安装三.pycharm上配置python四.配置镜像源让你下载嗖嗖的快4.1pycharm内部配置4.2手动添加镜像源4.3永久配置镜像源五.插件安装(比如汉化?)5.1自动补码神器第一款5.2汉化pycharm5.3其它插件六.美女背景七.自定义脚本开头八、这个前言一定要看九、python入门十、python缩进十一、Python注释1.单行注释2.多行注释十二、Python 变量1.变量定义理解2. 变量名命名3. 分

全栈川川 26万+

解密Android开机动画消失的瞬间:SurfaceFinger与Launcher的交接艺术

本文深入解析Android开机动画消失的瞬间,揭示SurfaceFlinger与Launcher的精密协作机制。从bootanimation启动到Launcher接管显示的全过程,详细分析了属性服务在进程间通信中的关键作用,以及如何通过系统服务与UI进程的协同实现无缝过渡。

weixin_29246195的博客 707

下采样新突破!ADown模块让YOLOv11多尺度特征提取更精准

ADown 凭借可编程梯度信息与多分支特征聚合的创新设计,打破了传统下采样在特征保留上的固有局限。本教程从原理到代码实现,再到工业场景适配,完整呈现了其在 YOLOv11 中的集成路径。只需简单新增模块脚本并修改配置文件,你的目标检测模型就能在多尺度特征捕捉能力上实现跨越式提升——无论是学术研究中的小目标检测突破,还是工业场景下的复杂多尺度任务落地,ADown 都能成为你攻克下采样瓶颈的利器。现在就动手尝试,让你的检测模型在下一代下采样技术的创新中再上一个台阶吧。

know_F0727的博客 524

2篇1章1节:数据的基本概念以及 R 中的数据结构、向量与矩阵的创建及运算

本文详细介绍了R语言中常见的数据结构的向量和矩阵。通过掌握这些数据结构的基本概念和操作技巧,可以更高效地处理和分析数据。

MD Code 1243

Android 14 开机动画启动、执行、退出流程分析附带开机黑屏问题修改(一)

最近解了一个关于开机进入主页前会闪下黑屏的问题,当时看网上基本所有的解决方案都是修改WindowManagerService、ActivityRecord、FallbackHome这个三者,实现在主页启动后才结束开机动画,由于当时做的是EDLA项目,这一修改触及到了Surface且framework层改动较大,容易引发fail项,就想着对开机动画的播放进行逐帧延长,达到上述效果,最后也是实现了上述效果。

weixin_53527829的博客 693

从“能跑”到“好用”:一名 Python 开发者的工程化进阶之路【Python心得篇】

Python 早已不只是写脚本、跑数据的工具。后端服务AI Agent 编排层自动化平台核心语言数据与业务之间的“中枢层”区别只在于:你是用脚本方式,还是工程方式在写。如果你已经能写“能跑的 Python”,下一步,就是让它长期可跑、稳定可演进。Python 的真正价值不在于“写得快”,而在于能否支撑持续演进的工程实践。当代码从一次性脚本走向长期运行的系统,开发者必须从结构设计、职责拆分、性能意识和类型约束等方面进行系统性思考。

博客 1546

Android8 开机动画结束分析

查了一下发现算是一个Android的设计吧,在这个xxx_main.cpp里是入口,然后在main方法里,new一个BootAnimation来进行调用,入口和逻辑相互分离。再往下会执行ReadyToRun方法,其实注释上也有写创建了一个native surface相当于画布,然后初始化opengl,然后是加密动画相关的代码跳过了,然后判断这两个地方有没有文件。3.在bootanimation_main.cpp的main方法里呢,就是开机动画的调用地方了。这是一个switch case,在这里面调用了。

v_3483608762的博客 249

FMC ANSIVITA_57.1.rar_FMC标准_VITA57.1_VITA57.1下载_fmc fpga_wheat1t

FMC接口标准文件,用于FMC硬件电路设计和FPGA程序约束编写

实证分析-上市公司绿色投资数据(1999-2024年)数据、代码及结果.txt

因文件较多,数据存放网盘,txt文件内包含下载链接及提取码,永久有效。失效会第一时间进行补充。样例数据及详细介绍参见文章:https://blog.youkuaiyun.com/T0620514/article/details/154186334

Android8开机动画,1:bootanimation的启动流程

..init进程会根据 init.rc中的配置,在系统启动时触发Surfaceflinger的启动。(SurfaceFlinger的主要任务:1:初始化图形硬件2:创建显示缓冲区3:管理显示内容的合成和输出这里init.rc文件应该也会触发bootanim.rc文件,但是bootanim.rc文件中设置了...disable (在开机时不会启动)...bootanim的主要任务:1:通过SurfaceFlinger创建显示Surface;

Sl3000_1的博客 1007

数字微波通信-.ppt

数字微波通信-.ppt

Android 9.0开机动画源码拆解与bootanimation.zip定制实践

Android系统启动流程中,开机动画是用户能直接感知的视觉环节。从原理上看,它并不是由init直接拉起,而是要等SurfaceFlinger就绪后,由独立进程BootAnimation连接图形系统,并通过bootanimation.zip内的desc.txt控制章节播放与退出条件。理解这一机制,能有效排查开机黑屏、动画卡死、闪帧等常见问题,也让ROM定制和产品验证更加可控。基于Android 9.0的源码梳理和打包规范,覆盖从触发时机、资源结构到问题定位的完整路径,为定制开机动画或研究Android启动链

weixin_32821533的博客 257

罗德施瓦茨示波器远程控制SCPI指令集

可以参考编写罗德施瓦茨示波器远程控制软件

上一篇: Android -- AdapterService之蓝牙启动过程分析
下一篇: Android -- WiFi的NUD检测机制浅析
第一序列丶
博客等级 码龄11年 249粉丝 77原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值