首页 > 其他分享 >Android 10第一次开机进Launcher闪白屏

Android 10第一次开机进Launcher闪白屏

时间:2023-09-07 22:37:28浏览次数:62  
标签:10 java 闪白屏 ... Launcher 开机 android com


一、背景说明

  • 问题描述:软件第一次开机或恢复出厂设置后开机,进入原生Launcher前闪现白屏,由于显示时间较短(约500ms),白屏界面未显示完整(1/3~1/2屏幕大小)就消失了。
  • Android版本:Android 10
  • 关键词:Android 10、开机、白屏

二、问题分析

所涉及的代码及其路径汇总如下:

packages\apps\Launcher3\src\com\android\launcher3\Launcher.java
packages\apps\Launcher3\AndroidManifest.xml
packages\apps\Settings\src\com\android\settings\FallbackHome.java
packages\apps\Provision\src\com\android\provision\DefaultActivity.java
frameworks\base\core\java\android\app\ApplicationPackageManager.java
frameworks\base\services\java\com\android\server\SystemServer.java
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java
frameworks\base\services\core\java\com\android\server\wm\RootActivityContainer.java
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

从问题现象来看,可以有两种可能性

  • 1.Launcher应用本身的问题,oncreate()前先显示了某个界面。
  • 2.系统原因或者其他应用问题,在进入Launcher前先打开了某个应用或界面。

显然2的可能性更大,原因在于白屏仅在第一次和恢复出厂设置后才出现,而普通的开关机,recovery不会出现,这很容易联系到开机向导应用,我们知道开机向导主要用于对系统的语言,网络等配置进行设置,而且仅在系统第一次启动时出现,优先于Launcher显示,这些行为很符合问题的特征,但需要’证据‘支持。 通常情况下,结合开机log分析开机启动顺序,比较容易定位到问题,但由于我的平台开机时跑完bootanimation会断开adb,并且没有UART串口,故没有办法抓取完整的开机log,和闪白屏log。

对于1情况,比较好容易验证,在Launcher的onCreate();中添加打印及延时,确认白屏与Launcher显示的顺序。 packages\apps\Launcher3\src\com\android\launcher3\Launcher.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        super.onCreate(savedInstanceState);
        try {
                Log.d(TAG,"delay 5 s");
                Thread.sleep(5000);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        ...
        setContentView(mLauncherView);
        ...
    }

验证发现,任然会闪白屏。闪白屏后延迟了5s的黑屏进入了Launcher,因此可以断定闪白屏在前,进入Launcher在后。 将上述延时移至FallbackHome packages\apps\Settings\src\com\android\settings\FallbackHome.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		...
        try {
                Log.d(TAG,"delay 5 s");
                Thread.sleep(5000);//添加5s延时
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
       ...
        mWallManager = getSystemService(WallpaperManager.class);
        if (mWallManager == null) {
            Log.w(TAG, "Wallpaper manager isn't ready, can't listen to color changes!");
        } else {
            loadWallpaperColors(flags);//加载壁纸
        }
        getWindow().getDecorView().setSystemUiVisibility(flags);
        registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
        maybeFinish();//视情况Finish自身Activity
    }

而现象为开机动画播完后黑屏5s后闪白屏才进入Launcher,所以是先启动FallbackHome界面。那么问题来了:

  • 为什么是先进入FallbackHome,而不是Launcher?
  • FallbackHome具体是怎么打开的,它起什么作用呢?
  • 闪白屏由FallbackHome引起? 对分析FallbackHome发现他使用的是系统壁纸,而系统使用定制的纯黑色背景,因此不会出现白屏,可以排除。那么'bug'到底藏在哪呢? 前面说过还有一个可疑点——开机向导。 于是乎,屏蔽平台的开机向导apk后恢复出厂设置进行验证。
# mv system/product/priv-app/Provision/Provision.apk system/product/priv-app/Provision/Provision.apk.1
# sync

恢复出厂设置后开机发现不再闪白屏,反复对比验证可以确定就是Provosion导致的闪白屏,至于具体的原因,我们还得从Launcher的启动流程开始分析。

三、Launcher启动流程

Launcher启动整体流程如下:

Android 10第一次开机进Launcher闪白屏_java

系统开机时init进程启动systemserver,这里从systemserver进程开始分析,之前的启动过程不再赘述。 frameworks\base\services\java\com\android\server\SystemServer.java

private void run() {
        try {
        ...
        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();//启动引导服务,如AMS、PMS、PKMS、DMS等
            startCoreServices();//启动系统核心服务,如BatteryService、WebViewUpdateService等
            startOtherServices();//启动其他服务,如IMS,WMS,GpuService等
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
        ...
        }
	}

	private void startBootstrapServices() {
	    ...
	    // Activity manager runs the show.
        traceBeginAndSlog("StartActivityManager");
        // TODO: Might need to move after migration to WM.
        ActivityTaskManagerService atm = mSystemServiceManager.startService(
            ActivityTaskManagerService.Lifecycle.class).getService();//启动ATMS服务
        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
            mSystemServiceManager, atm);//启动AMS服务
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        mWindowManagerGlobalLock = atm.getGlobalLock();
        traceEnd();
	    ...
	}

	private void startOtherServices() {
	...
		mActivityManagerService.systemReady(() -> {
	...
	}

从Android 10(API29)开始,ActivityManagerService的工作被ActivityTaskManagerService接管,虽然AMS被接管,但为例保持不同版本系统的正常运行,AMS依然可用,原有接口依然有用。只是有些接口被注解为@Deprecated ,代码跳转至ATMS执行,例如startActiviy():

@Override
    public int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
    }

调用AMS的systemReady(),准备启动Launcher frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
	...
	if (bootingSystemUser) {
        mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
    }
	...
}

mAtmInternal 是ActivityTaskManagerInternal在ATMS的构造方法中初始化,是由ATMS对外提供的一个抽象类,真正的实现是在ATMS中的 LocalService,所以执行到了 LocalService 的 startHomeOnAllDisplays方法。 frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java

final class LocalService extends ActivityTaskManagerInternal {
	...
	@Override
    public boolean startHomeOnAllDisplays(int userId, String reason) {
        synchronized (mGlobalLock) {
            return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
        }
    }
	...
}

frameworks\base\services\core\java\com\android\server\wm\RootActivityContainer.java

boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
            final int displayId = mActivityDisplays.get(i).mDisplayId;
            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
        }
        return homeStarted;
    }
    ...
    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
            false /* fromHomeKey */);
    }
    boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
        ...
        if (displayId == DEFAULT_DISPLAY) {
            homeIntent = mService.getHomeIntent();//构建一个category为CATEGORY_HOME的Intent,表明是Home Activity
            aInfo = resolveHomeActivity(userId, homeIntent);//通过PKMS从系统所用已安装的应用中,找到一个符合HomeItent的Activity
        } 
        ...
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                displayId);//启动Home Activity
        return true;
    }

frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java

Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

从上面代码分析可知,通过构建category为CATEGORY_HOME的Intent,与PKMS已安装的应用中找到一个匹配的aInfo,并准备启动。讲道理aInfo应当为Launcher。 packages\apps\Launcher3\AndroidManifest.xml

<activity
            android:name="com.android.launcher3.Launcher"
            ...
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                ...
        </activity>

但事实上通过打印aInfo,发现有限启动的是FallbackHome,原来在安装的apk中,除了Launcher,还有以下应用有配置android.intent.category.HOME packages/apps/Provision/AndroidManifest.xml

<application>
        <activity android:name="DefaultActivity"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                ...
    </application>

packages/apps/Settings/AndroidManifest.xml

<activity android:name=".CryptKeeper"
                  ...
            <intent-filter android:priority="10">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <!-- Triggered when user-selected home app isn't encryption aware -->
        <activity android:name=".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>

奇怪的是,从android:priority属性来看,优先级顺序应该是CryptKeeper>DefaultActivity>Launcher>FallbackHome,但事实上启动的顺序是FallbackHome>DefaultActivity>Launcher。 CryptKeeper负责锁屏相关,系统第一次开机并未进行锁屏,故不会启动。至于为什么FallbackHome优先启动呢? 顺着resolveHomeActivity()往下看... frameworks\base\services\core\java\com\android\server\wm\RootActivityContainer.java

@VisibleForTesting
    ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
        final ComponentName comp = homeIntent.getComponent();
        ActivityInfo aInfo = null;
        try {
            if (comp != null) {
                // Factory test.
                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
            } else {
                final String resolvedType =
                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
                final ResolveInfo info = AppGlobals.getPackageManager()
                        .resolveIntent(homeIntent, resolvedType, flags, userId);
                if (info != null) {
                    aInfo = info.activityInfo;
                }
            }
        } 
        ...
        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
        return aInfo;
    }

frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

@Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId) {
        return resolveIntentInternal(intent, resolvedType, flags, userId, false,
                Binder.getCallingUid());
    }

	private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
            int flags, int userId, boolean resolveForStart, int filterCallingUid) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
            ...
            //查询所有符合条件的activities
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                    flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            final ResolveInfo bestChoice =
                    chooseBestActivity(intent, resolvedType, flags, query, userId);//从query 中选择最适的启动Activity
            return bestChoice;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

通过打印发现query 只有FallbackHome,因此FallbackHome最新启动。 packages\apps\Settings\src\com\android\settings\FallbackHome.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        //注册用户解锁的广播
        registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
        maybeFinish();
    }
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            maybeFinish();//接收到广播后执行maybeFinish()
        }
    };

	private void maybeFinish() {
        if (getSystemService(UserManager.class).isUserUnlocked()) {
            final Intent homeIntent = new Intent(Intent.ACTION_MAIN)
                    .addCategory(Intent.CATEGORY_HOME);
            //最终调用resolveIntent()再次获取合适的HomeActivity
            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();//finish()自身
            }
        }
    }

由于开机向导的优先级高于Launcher,故FallbackHome启动DefaultActivity packages\apps\Provision\src\com\android\provision\DefaultActivity.java

public class DefaultActivity extends Activity {

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);

        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();
        ComponentName name = new ComponentName(this, DefaultActivity.class);
        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
        // terminate the activity.
        finish();
    }
}

可以看到系统开机向导内容很简单,设置DEVICE_PROVISIONED和USER_SETUP_COMPLETE表示系统完成开机设置,通常用于拉起app。另外就是diable自身,后续开机不会启动,并finish()自身。后续则启动Launcher,走正常的应用启动流程了,后续过程不在赘述了。 自此,从现象,结合log及系统启动流程,理清了白屏产生的机制,那么如何解决该问题呢?

  • 方法一、 修改如下: packages/apps/Provision/AndroidManifest.xml
<application>
        <activity android:name="DefaultActivity"
    +       	android:directBootAware="true"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                ...
    </application>

由于FallbackHome启动后需要接受到用户取消解锁广播后才finish自己,启动下一个homeActivity,这样就引起了白屏闪现。因此打开Provision 的directBootAware属性,使其可直接启动。

  • 方案二、 移除Provision开机向导,并将DefaultActivity中的DEVICE_PROVISIONED和USER_SETUP_COMPLETE移到Launcher中或者其他开机启动的apk。 device/qcom/common/base.mk
PRODUCT_PACKAGES := \
    AccountAndSyncSettings \
    DeskClock \
    ...
-   Provision \
    ...

总结

通过Launcher启动的代码流程分析,配合log验证,梳理了FallbackHome、Launcher、Provision三者之间的启动优先级,并锁定白屏所在应用,找到合适的规避方法。

标签:10,java,闪白屏,...,Launcher,开机,android,com
From: https://blog.51cto.com/u_12727059/7402040

相关文章

  • 统信UOS1060使用UDOM工具箱打开支持长文件名特性
    原文链接:统信UOS1060使用UDOM工具箱打开支持长文件名特性hello,大家好啊,今天给大家带来一篇文章,在统信UOS发布的最新版桌面操作系统1060中,增加了长文件名模式,最长支持255个中文或英文字符,这样对于在windows上使用长文件的文件,迁移到国产操作系统上而言,避免了文件命名失败、文件丢失......
  • 网络错误码 10013 错误问题分析
    前言10013以一种访问权限不允许的方式做了一个访问套接字的尝试。 原因绑定一个处于CLOSE_WAIT状态的端口,会产生该错误重现先找到一个处于CLOSE_WAIT状态的端口49724C:\Users\admin>netstat-ano|findstrCLOSE_WAITTCP192.168.11.149:49724123.60.175.170:80CLO......
  • 用友GRP-U8 Proxy SQL注入 CNNVD-201610-923
    漏洞描述用友GRP-u8存在XXE漏洞,该漏洞源于应用程序解析XML输入时没有进制外部实体的加载,导致可加载外部SQL语句,以及命令执行影响版本用友GRP-U8行政事业内控管理软件(新政府会计制度专版)漏洞复现fofa语法:title="用友GRP-U8行政事业内控管理软件"登录页面:POC:POST/ProxyHT......
  • 中芯微和高通410的随身WiFi哪种好,随身WiFi怎么刷机
    中兴微比高通410好的地方,对我个人而言有3点这款棒子是中兴微很赞,比我之前的高通410棒子好的地方主要是4点:1。这款中兴微棒子,不需要切卡密码,插卡直接就切了,2。能在网页就看收到的短信,不需要装短信APP,用电脑进投屏,也不用折腾短信转发(虽然也不能短信转发)3。能给子设备分配公网ipv6(......
  • 外汇110网:交易止损里的“门道”:计划止损与突发止损
    止损是投资者必备的交易技巧。一般来说,最后的亏损是因为没有执行盈亏比3:1设定的止损而产生计划外超额止损,而这种损失大多就是来自所谓的突发性止损。那么,计划止损与突发止损这两种止损方式究竟有什么不同,我们又应该采取什么策略去处理和应对呢? 计划止损 单从表面意思看,计划止损就......
  • 离线安装docker docker-20.10.6
    离线安装dockerDocker是一个开源的应用容器引擎,它让开发者能够将应用打包在一个可移植的容器中,然后发布到任何流行的Linux机器或Windows机器上。通过使用Docker,开发者可以创建、部署和运行应用程序,而无需担心基础设施的问题。然而,在一些特定的环境下,如限制访问互联网的内部网络或外......
  • 使用JS,IE提示:由于出现错误 80020101 而导致此项操作
    消息:由于出现错误80020101而导致此项操作无法完成。行:7字符:36748代码:0URI:http://localhost:3760/Ext_Demo/ext/ext-all.js问题原因:这个错误通常是在执行某些语句时,相应的引用文件还没有加载到浏览器,导致无法执行。可以检查一下加载顺序的问题(引用或者iframe)和执行权限......
  • 08:49:45,218 WARN JDBCExceptionReporter:71 - SQL Error: 156, SQLState: S1000 关
    昨晚运行以前的一个项目,在初始化数据的时候报:08:49:45,218 WARNJDBCExceptionReporter:71-SQLError:156,SQLState:S100008:49:45,218ERRORJDBCExceptionReporter:72-关键字'user'附近有语法错误。org.hibernate.exception.GenericJDBCException:couldnotexecute......
  • 外汇110网实地探访澳洲EightCap ·易汇办公场所!
    近期,不少平台出现不出金情况,其中交易者常常因为平台恶意诱导、恶意爆仓、甚至无故缴纳出金税款等恶劣行为困扰。负面现象持续高发,受害人也叫苦不迭,这些案例与教训希望能给世人以警示与思索。那么EightCap·易汇到底靠不靠谱呢?根据Eightcap易汇官网信息介绍,其品牌成立于2009年,擅......
  • win10 wsl 运行后没有反应
    wsl运行一段时间后执行没有反应,需要重启LxssManager管理员模式打开powshell找到pid,结束pid>tasklist/svc/fi"serviceseqLxssManager"映像名称PID服务=============================================================================s......