Problem introduction
During the development of the new platform, the test students always complained that the screen would flash black for about 2-3s before entering the Launcher, which would affect the user experience. At first, I didn't pay special attention. I thought there were too many things loaded during the start of Activity, resulting in a slow rendering of the form. After follow-up analysis, it turns out that there are other reasons, and several knowledge points are involved. mark here. (on another platform, there is a display of "tablet is starting up", and students of the product want to remove or replace it, so we can achieve it? After reading this article, we will have the answer.)
discover problems
Grab the boot print and check what the system did before and after entering the Launcher:
12-17 15:22:57.358 507 507 I ActivityManager: System now ready 12-17 15:22:58.702 507 561 I ActivityManager: Start proc 938:com.android.tv.settings/1000 for top-activity {com.android.tv.settings/com.android.tv.settings.system.FallbackHome} 12-17 15:23:01.076 507 554 E ActivityManager: ams finishBooting. 12-17 15:23:01.078 507 554 I ActivityManager: User 0 state changed from BOOTING to RUNNING_LOCKED 12-17 15:23:01.118 507 554 D ActivityManager: Started unlocking user 0 12-17 15:23:01.118 507 554 D ActivityManager: Unlocking user 0 progress 0 12-17 15:23:01.118 507 554 D ActivityManager: Unlocking user 0 progress 5 12-17 15:23:01.869 507 551 I ActivityManager: User 0 state changed from RUNNING_LOCKED to RUNNING_UNLOCKING 12-17 15:23:01.870 507 551 D ActivityManager: Unlocking user 0 progress 20 12-17 15:23:06.422 507 560 I ActivityManager: User 0 state changed from RUNNING_UNLOCKING to RUNNING_UNLOCKED 12-17 15:23:06.456 507 560 I ActivityManager: Posting BOOT_COMPLETED user #0 12-17 15:23:06.602 507 561 I ActivityManager: Start proc 1274:com.funshion.ottedu/u0a26 for top-activity {com.funshion.ottedu/com.bestv.ott.home.HomeActivity}
We found that a {com.android.tv.settings/com.android.tv.settings.system.FallbackHome} component was started before launching our Launcher application (COM. Fushion. Ottedu), and it was started before unlocking the screen.
FallbackHome
After searching, it is found that the directBootAware property is set in the native settings, and the FallbackHome has Android intent. category. Home feature, but low priority (- 1000)
packages/apps/Settings/AndroidManifest.xml
android:directBootAware="true" <activity android:name=".FallbackHome" android:excludeFromRecents="true" android:label="" android:screenOrientation="nosensor" android:taskAffinity="com.android.settings.FallbackHome" android:theme="@style/FallbackHome"> <intent-filter android:priority="-1000"> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
public class FallbackHome extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); maybeFinish(); } private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { maybeFinish(); } }; private void maybeFinish() { if (getSystemService(UserManager.class).isUserUnlocked()) { final Intent homeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME); final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0); if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) { if (UserManager.isSplitSystemUser() && UserHandle.myUserId() == UserHandle.USER_SYSTEM) { // This avoids the situation where the system user has no home activity after // SUW and this activity continues to throw out warnings. See b/28870689. return; } Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?"); mHandler.sendEmptyMessageDelayed(0, 500); } else { Log.d(TAG, "User unlocked and real home found; let's go!"); getSystemService(PowerManager.class).userActivity( SystemClock.uptimeMillis(), false); finish(); } } } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { maybeFinish(); } }; }
The code of FallbackHome is very simple. The simple interface shows that "the platform is starting", and then wait for the broadcast unlocked by the user. After receiving it, judge whether a new Launcher is available and end your mission. To be exact, it is just a transition interface.
The reason for the black screen on the new platform is that the FallbackHome interface is displayed after a delay of 2s. When we are about to display the FallbackHome interface, the black screen appears when we receive the system unlock (the system does not set the ping password and directly enter the system without unlocking)
Then there are two solutions to our problem, which will be supplemented in detail later.
directBootAware
Direct Boot Mode - a new feature introduced by AndroidN. The device enters after startup until the user unlock s the device. The application can start in the user lock screen stage and handle part of the logic of the application. The biggest difference between Direct Boot Mode and normal mode is the use of a new storage space: Device protected storage.
Android bright screen process
This is a complicated and tedious process. Here are some key files and functions. Interested friends can view the source code themselves (they may write a separate blog post to share later):
frameworks/base/services/java/com/android/server/SystemServer.java frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java //AMS systemReady public systemReady() { Slog.i(TAG, "System now ready"); //FallbackHome will be pulled up here startHomeActivityLocked(currentUserId, "systemReady"); } frameworks/base/core/java/android/app/ActivityThread.java //In the handleResumeActivity method of the resume stage, after loading the window, add the Idlehandler implemented by yourself to your message queue. handleLaunchActivity{ Looper.myQueue().addIdleHandler(new Idler()); } //Here, call the activityIdle of AMS to start the bright screen process MessageQueue.IdleHandler { am.activityIdle(a.token, a.createdConfig, stopProfiling); } //activityIdle finally calls the activityIdleInternalLocked in ActivityStackSuperVisor. //It will report the end of the startup time, and check whether it is still in the startup phase, and then end the startup process. It means that the desktop has been displayed in focus, and the boot animation can be retired. ActivityRecord activityIdleInternalLocked() { if (isFocusedStack(r.getStack()) || fromTimeout) { booting = checkFinishBootingLocked(); } } private boolean checkFinishBootingLocked() { mService.postFinishBooting(booting, enableScreen);//This is called into AMS } //Back to AMS frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java void postFinishBooting(boolean finishBooting, boolean enableScreen) { mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG, finishBooting ? 1 : 0, enableScreen ? 1 : 0)); } 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; } //Send bright screen request void enableScreenAfterBoot() { mWindowManager.enableScreenAfterBoot(); } frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java //Bright screen processing public void enableScreenIfNeeded() { synchronized (mWindowMap) { enableScreenIfNeededLocked(); mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);//Timeout 30s } } void enableScreenIfNeededLocked() { mH.sendEmptyMessage(H.ENABLE_SCREEN); } private void performEnableScreen() { if (!mBootAnimationStopped) { //Setting properties here can turn off the startup animation SystemProperties.set("service.bootanim.exit", "1"); mBootAnimationStopped = true; } mActivityManager.bootAnimationComplete();//Here's back to AMS mPolicy.enableScreenAfterBoot(); } //Return to AMS again frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java //After the screen is lit, inform AMS of subsequent operations public void bootAnimationComplete() { finishBooting(); } //The first application of the system starts, the window is ready, the boot animation is introduced, and the boot broadcast is sent here final void finishBooting() { mUserController.sendBootCompleted()//There will be unlocking broadcasting and so on } frameworks/base/services/core/java/com/android/server/am/UserController.java void sendBootCompleted(IIntentReceiver resultTo) { finishUserBoot(uss, resultTo); } private void finishUserBoot(UserState uss, IIntentReceiver resultTo) { } void finishUserUnlocked(final UserState uss) { final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); mInjector.sendPreBootBroadcast(userId, quiet, () -> finishUserUnlockedCompleted(uss)); }
It's messy. I didn't draw a picture. I'll analyze it slowly and in detail later. Friends who don't pay attention can be ignored.
Solution
1. Some platforms do not display a black screen, but display "the tablet is starting". We can directly modify the FallbackHome interface to the effect you want. Of course, you can remove the FallbackHome interface. But the premise is that you need to implement a belt
The directBootAware attribute is applied and has Android intent. category. The Activity of the home attribute. Otherwise, the system will not work. Because the system cannot light up
2. Delay the startup animation and kill the startup animation after your Launcher gets up.
The patch is directly given here
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 9c8b7ef..e1c4e14 100755 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1161,19 +1161,25 @@ bool BootAnimation::android() glDeleteTextures(1, &mAndroid[1].name); return false; } - void BootAnimation::checkExit() { // Allow surface flinger to gracefully request shutdown char value[PROPERTY_VALUE_MAX]; + char value1[PROPERTY_VALUE_MAX]; + char value2[PROPERTY_VALUE_MAX]; property_get(EXIT_PROP_NAME, value, "0"); + property_get("service.launcher.visible", value1, "0"); + property_get("service.launcher.create", value2, "0"); ALOGI("service.bootanim.exit : %s", value); + ALOGI("service.launcher.visible : %s", value1); + ALOGI("service.launcher.create : %s", value2); int exitnow = atoi(value); - if (exitnow) { + if (exitnow && (atoi(value1)||atoi(value2))) { + ALOGI("checkExit really exit"); + property_set("service.boot.complete", "1"); requestExit(); mCallbacks->shutdown(); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4b98d02..b83f733 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3485,12 +3485,12 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean checkBootAnimationCompleteLocked() { - if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) { + if (!mDisplayEnabled) { mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED); mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED, BOOT_ANIMATION_POLL_INTERVAL); if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Waiting for anim complete"); - return false; + return mBootAnimationStopped; } if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Animation complete!"); return true; --
Why do I need to modify checkBootAnimationCompleteLocked in WindowManagerService. The bootAnimationComplete will not be sent to AMS until the boot animation is completed. If the unlock broadcast is not received, FallbakHome will run all the time. (WMS also has a timeout mechanism, 30s).
They also have HOME. Why is FallbackHome found first
You must have this question. There is something fishy here because:
Before unlocking, queryIntentActivitiesInternal() finds HomeIntent, which can only match FallbackHome (independent of priority).
android/content/pm/PackageUserState.java public boolean isMatch(ComponentInfo componentInfo, int flags) { final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp(); final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0; if (!isAvailable(flags) && !(isSystemApp && matchUninstalled)) return false; if (!isEnabled(componentInfo, flags)) return false; if ((flags & MATCH_SYSTEM_ONLY) != 0) { if (!isSystemApp) { return false; } } final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !componentInfo.directBootAware; final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && componentInfo.directBootAware; return matchesUnaware || matchesAware; }
We won't continue here. The key code has been posted. Go to RFSC yourself.