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

本文详细解析了Android系统如何在启动过程中触发Bootanimation的退出,从ActivityManagerService的systemReady()开始,经过一系列流程,最终通过SurfaceFlinger的bootFinished()方法设置属性并停止bootanimation,显示Launcher界面。这一过程涉及到系统服务、进程管理、UI显示等多个关键环节。

目录

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界面

 

 

 

<think>我们正在讨论Android 14开机Logo的显示逻辑。根据引用资料,我们可以将开机Logo的显示分为几个阶段:内核logo(由bootloader显示)和用户空间的开机动画(由bootanimation或SurfaceFlinger显示)。在Android系统中,开机Logo通常指的是从内核启动到用户空间启动过程中显示的静态或动态画面。 根据引用[1],系统开机过程默认会显示一个Android画面,这个画面是由bootanimation程序显示的动画。如果系统指定目录下存在自定义动画文件(.zip),则显示自定义动画,否则显示默认的明暗闪烁的Android Logo。 引用[3]提到,在内核阶段(LK阶段,即Little Kernel,常见于MTK平台),会显示一个logo,这个logo是将bmp图片转换为raw格式,存放在`vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\`目录下。 引用[4]和[5]则详细说明了用户空间开机动画的播放方式,即通过`bootanimation`程序,有两种方式:一种是系统默认的动画(`android()`函数),另一种是用户自定义的动画(`movie()`函数)。 在Android 14中,开机Logo的显示机制应该也是类似的。不过,从Android 10开始,Google引入了新的开机动画显示方式,即通过SurfaceFlinger来启动开机动画(称为BootAnimation),但基本逻辑没有太大变化。 现在,我们重点分析Android 14的开机Logo显示逻辑,包括内核阶段和用户空间阶段。 ### 一、内核阶段(Bootloader阶段)的Logo显示 在内核启动阶段,通常由Bootloader(如LK、U-Boot等)负责显示一个静态的Logo。这个Logo是一个未压缩的位图(raw格式),直接写入帧缓冲区(framebuffer)。以MTK平台为例,其路径在: ``` vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo/ ``` 其他平台可能有所不同,但原理类似。 ### 二、用户空间阶段(init进程到SurfaceFlinger启动)的Logo显示 当内核启动完成后,会启动用户空间的第一个进程init。init进程会启动SurfaceFlinger、bootanimation等关键服务。在Android 14中,开机动画的显示由`bootanimation`程序负责,该程序位于: ``` frameworks/base/cmds/bootanimation/ ``` #### 1. bootanimation的启动 在init进程中,会解析init.rc文件,其中会启动bootanimation服务。在Android 14中,bootanimation服务的启动脚本位于: ``` system/core/rootdir/init.rc ``` 在该文件中,我们可以找到如下代码: ``` service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio disabled oneshot ioprio rt 0 task_profiles MaxPerformance ``` 当系统属性`init.svc.bootanim`被设置为`start`时,该服务就会启动。通常,这个属性在`SurfaceFlinger`启动后由`SurfaceFlinger`设置。 #### 2. bootanimation的源码逻辑 bootanimation的主要源码在: - `frameworks/base/cmds/bootanimation/bootanimation_main.cpp` - `frameworks/base/cmds/bootanimation/BootAnimation.cpp` 在`bootanimation_main.cpp`中,`main`函数会启动一个线程,然后进入主循环。在`BootAnimation.cpp`中,`threadLoop()`函数是核心。 在`threadLoop()`函数中,会检查是否有自定义动画(即`/system/media/bootanimation.zip`或`/product/media/bootanimation.zip`),如果有,则调用`movie()`播放自定义动画;否则,调用`android()`播放默认动画。 ```cpp // frameworks/base/cmds/bootanimation/BootAnimation.cpp bool BootAnimation::threadLoop() { // 等待SurfaceFlinger启动 if (mZipFileName.isEmpty()) { r = android(); // 播放默认动画 } else { r = movie(); // 播放自定义动画 } return false; } ``` #### 3. 默认动画android()函数) 默认动画是使用OpenGL ES绘制的一个简单的动画,通常是一个“Android”文字,并伴有明暗闪烁效果。该动画的绘制在`android()`函数中实现。 #### 4. 自定义动画(movie()函数) 自定义动画是通过解析一个zip文件(bootanimation.zip)来实现的。这个zip文件包含一系列图片和一个描述文件`desc.txt`。`desc.txt`中定义了动画的播放参数,如分辨率、帧率、循环次数等。 ### 三、Android 14中开机Logo的显示流程 1. **Bootloader阶段**:显示内核Logo(静态图片)。 2. **内核启动完成后**:init进程启动,并启动SurfaceFlinger等关键服务。 3. **SurfaceFlinger启动后**:设置属性`init.svc.bootanim=start`,从而触发bootanimation服务启动。 4. **bootanimation启动**:检查是否有自定义动画文件,如果有则播放自定义动画,否则播放默认动画。 5. **系统启动完成**:当系统启动完成后,ActivityManagerService会发送`BOOT_COMPLETED`广播,并停止开机动画(通过设置`service.bootanim.exit`属性为1)。 ### 四、源码关键点 1. **bootanimation退出**:当系统启动完成时,ActivityManagerService会设置`service.bootanim.exit`属性为1。bootanimation会监听这个属性,一旦发现为1,则退出动画播放。 2. **横屏显示**:根据引用[2],如果需要默认横屏显示,需要修改多个地方,包括`BootAnimation.cpp`(旋转画面)和`SurfaceFlinger`的显示设置。 ### 五、Android 14中可能的变化 在Android 14中,Google可能对开机动画的显示机制做了一些优化,但整体流程没有太大变化。需要注意的是,从Android 10开始,部分设备采用了新的显示方式,即由SurfaceFlinger直接启动开机动画,但bootanimation的核心逻辑仍然保留。 ### 六、流程图 ```mermaid graph TD A[Bootloader显示内核Logo] --> B[内核启动] B --> C[init进程启动] C --> D[启动SurfaceFlinger] D --> E[设置属性启动bootanimation] E --> F{bootanimation检查动画文件} F -->|存在bootanimation.zip| G[播放自定义动画] F -->|不存在| H[播放默认动画] G --> I[系统启动完成] H --> I I --> J[设置service.bootanim.exit=1] J --> K[bootanimation退出] ``` ### 引用说明 以上流程基于Android 14源码以及引用[1][3][4][5]的分析[^1][^3][^4][^5]。 §§相关问题§§ 1. 如何修改Android 14的内核启动Logo? 2. 如何自定义Android 14的开机动画bootanimation.zip)? 3. Android 14中开机动画的横屏显示如何适配? 4. bootanimation.zip的desc.txt文件格式是怎样的? 5. 如何调试Android 14的开机动画显示问题?</think>### Android 14 开机Logo显示机制及源码逻辑 Android 14 的开机Logo显示分为两个阶段,核心源码逻辑如下: --- ### 一、内核阶段(Bootloader Logo) 1. **显示位置**: Bootloader(如LK/U-Boot)直接向帧缓冲区写入原始图像数据 ```c // vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo/logo.c void display_logo(logo_header *header) { uint32_t *dst = fb_addr; // 帧缓冲地址 uint32_t *src = (uint32_t*)(header + 1); // 图像数据 memcpy(dst, src, header->width * header->height * 4); } ``` [^3] 2. **图像格式**: - 使用未压缩的RAW格式(如RGB888) - 位置:`vendor/.../bootloader/lk/dev/logo/` - 转换工具:`bmp_to_raw`(将BMP转为设备可识别的原始格式)[^3] 3. **显示流程**: ```mermaid graph LR A[Bootloader启动] --> B[初始化显示驱动] B --> C[加载logo.raw] C --> D[写入帧缓冲区] D --> E[显示内核Logo] ``` --- ### 二、用户空间阶段(Bootanimation) 当SurfaceFlinger服务启动后,由`bootanimation`进程接管显示: #### 1. 核心源码文件 - `frameworks/base/cmds/bootanimation/BootAnimation.cpp` - `frameworks/base/cmds/bootanimation/bootanimation_main.cpp` #### 2. 显示逻辑(`BootAnimation.cpp`) ```cpp bool BootAnimation::threadLoop() { // 1. 检查动画文件 findBootAnimationFile(); // 搜索/system/media/bootanimation.zip // 2. 选择播放模式 if (mZipFileName.isEmpty()) { android(); // 系统默认动画 } else { movie(); // 自定义动画 } // 3. 监听退出信号 while (!exitPending()) { checkExit(); // 检测service.bootanim.exit属性 } return false; } ``` [^4][^5] #### 3. 两种播放模式 **a. 默认动画 (`android()`)** 使用OpenGL ES绘制动态Android文字: ```cpp bool BootAnimation::android() { glDrawText("Android", x, y); // OpenGL渲染文字 eglSwapBuffers(); // 刷新显示 return true; } ``` **b. 自定义动画 (`movie()`)** 解析ZIP包播放帧序列: ```cpp bool BootAnimation::movie() { Animation* anim = loadAnimation(mZipFileName); while (playAnimation(*anim)) { if (shouldStopPlayingPart(part)) break; } releaseAnimation(anim); } ``` [^5] #### 4. 动画文件规则 - **路径**:`/system/media/bootanimation.zip` - **ZIP结构**: ``` desc.txt // 描述文件 part0/ // 动画帧目录 frame_00.png frame_01.png part1/ ... ``` #### 5. 横屏适配(Android 15引用) 在`DisplayRotation.cpp`中设置默认方向: ```java // frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java void setDefaultDisplayRotation(int rotation) { mDefaultRotation = rotation; // SurfaceFlinger.SCREEN_ORIENTATION_LANDSCAPE } ``` [^2] --- ### 三、完整启动流程 ```mermaid sequenceDiagram Bootloader->>+FrameBuffer: 显示内核Logo Kernel->>+init: 启动init进程 init->>+SurfaceFlinger: 启动显示服务 SurfaceFlinger->>+bootanimation: 启动动画进程 bootanimation->>+FrameBuffer: 检查动画文件 alt 存在bootanimation.zip bootanimation-->>FrameBuffer: 播放自定义动画 else 不存在 bootanimation-->>FrameBuffer: 渲染默认Android动画 end SystemServer->>bootanimation: 发送退出信号(service.bootanim.exit=1) bootanimation->>-FrameBuffer: 退出动画 ``` --- ### 四、关键配置点 1. **内核Logo替换** 替换`vendor/.../lk/dev/logo/`中的RAW文件[^3] 2. **自定义动画** 将ZIP包放入`/system/media/bootanimation.zip`,需包含: ```txt // desc.txt 示例 1080 1920 30 // 宽 高 帧率 p 1 0 part0 // 播放1次 part0目录 p 0 0 part1 // 循环播放 part1目录 ``` 3. **强制横屏**(引用Android 15实现) 修改`SurfaceFlinger.cpp`中的默认方向值[^2] --- ### 五、调试命令 ```bash # 查看动画日志 adb logcat -s BootAnimation # 强制重启动画 adb shell setprop service.bootanim.exit 0 && adb shell start bootanim ``` ### 引用说明 以上逻辑基于Android 14源码实现,横屏适配参考Android 15方案[^2][^5]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值