Actual combat - black screen problem before Android starts up and enters Launcher

Posted by VnVision on Sat, 18 Dec 2021 10:38:05 +0100

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.

Topics: Android tv