深入分析Android开机找网延迟
一般来说,phone的属性是persistent为true,而因此phone进程也是较早被叫起,被android:presistent修饰的应用在系统启动之后会被AM启动,即便没有运行,AM也会调用startProcessLocked启动该进程。启动package com.android.phone 即phone application,这会直接call到PhoneApp的onCreate(),执行初始化找网的动作。
我们查看源码如下:
alps\packages\services\Telephony\AndroidManifest.xml
android:persistent="true"
......
那么phone被正常叫起的log应该如下: 01-01 14:01:47.890: I/ActivityManager(717): Start proc com.android.phone for added application com.android.phone: pid=991 uid=1001 gids={41001, 3002, 3001, 3003, 1028, 1015, 1004, 2002, 1023}
从上面log中可以看到Phone进程正常叫起。但是我们去查看找网延迟的手机去复现的时候发现,Phone进程并不是正常叫起,而是被run在Phone进程中的组件所启动,参考log如下:
01-29 14:08:29.842: I/ActivityManager(723): Start proc com.android.phone for service com.mediatek.CellConnService/.PhoneStatesMgrService: pid=990 uid=1001 gids={41001, 3002, 3001, 3003, 1028, 1015, 1004, 2002, 1023}
从上面log中可以看到当前phone进程是由CellConnService.PhoneStatesMgrService服务叫起,我们查看PhoneStatesMgrService源码发现,该服务作为phone的一个组件运行在phone进程中。参考源码如下:
android:process="com.android.phone"//通过这个android:process="com.android.phone"属性,我们可以指定某个组件运行的进程。我们可以通过设置这个属性,让每个组件运行在它自己的进程中,也可以只让某些组件共享一个进程。我们要可以通过设置“android:process”属性,让不同应用程序中的组件运行在相同的进程中。这里com.mediatek.CellConnService作为一个组件运行在com.android.phone中
android:allowClearUserData="https://www.it610.com/article/false"
......
CELLCONNSERVICE" />
那么如果phone进程是由phone的接口叫起,那么就无法执行初始化找网的操作,而是等到alps\packages\services\telephony\AndroidManifest.xml 中OtaStartUpReceiver接收BOOT_COMPLETED后才能叫起PhoneApp,进而才执行它的onCreate()进行初始化找网的操作。这样无疑会导致找网延迟。那么此时我们需要排查哪里启动了PhoneStagtesMgrService。这里我们需要关注一下上面加粗标红的一个Action---CELLCONNSERVICE,然后我们接着分析log,参考log如下:
01-29 14:08:29.764: W/ContextImpl(882): Implicit intents with startService are not safe: Intent { act=android.intent.action.CELLCONNSERVICE } android.content.ContextWrapper.startService:494 com.mediatek.CellConnService.CellConnMgr.register:159 com.android.systemui.huawei.MobileStateManager.
01-29 14:08:29.852: W/ContextImpl(882): Implicit intents with startService are not safe: Intent { act=android.intent.action.CELLCONNSERVICE } android.content.ContextWrapper.bindService:517 com.mediatek.CellConnService.CellConnMgr.register:160 com.android.systemui.huawei.MobileStateManager.
01-29 14:08:32.294: W/ContextImpl(882): Implicit intents with startService are not safe: Intent { act=android.intent.action.CELLCONNSERVICE } android.content.ContextWrapper.startService:494 com.mediatek.CellConnService.CellConnMgr.register:159 com.android.systemui.huawei.MobileStateManager.
01-29 14:08:32.307: W/ContextImpl(882): Implicit intents with startService are not safe: Intent { act=android.intent.action.CELLCONNSERVICE } android.content.ContextWrapper.bindService:517 com.mediatek.CellConnService.CellConnMgr.register:160 com.android.systemui.huawei.MobileStateManager.
从上面的log我们看到MobileStateManager通过Intent启动了一个服务,而接收该Intent的服务恰恰是PhoneStagtesMgrService,参考代码如下:
代码一:此处时启动PhoneStatesMgrService的接口,位于CellConnServic下的CellConnMgr的类中。
public void register(Context ctx) {
Log.d(TAG, "register");
mCtx = ctx;
Intent it = new Intent("android.intent.action.CELLCONNSERVICE");
mCtx.startService(it);
mCtx.bindService(it, mConnection, Context.BIND_AUTO_CREATE);
}
代码二: 此处是systemui下的MobileStateManager中构造函数的调用
public MobileStateManager(Context context) {
mContext = context;
mCellConnMgr = new CellConnMgr(null);
//mCellConnMgr.register(mContext);
//这里调用了启动PhoneStatesMgrService服务的接口
mITelephony = getITelephony();
mConnManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
updateSIMInfoList();
}
因此我们在这里进行代码的优化,从而问题得到解决。
需要注意的是作为一个组件run在com.android.phone进程中的不只是com.mediatek.CellConnService,例如还有com.android.providers.telephony等,这里我也把com.android.providers.telephony叫起com.android.phone的分析方法与大家分享一下。
下面是通过调用com.android.providers.telephony叫起com.android.phone进程的log,如下:
01-30 11:02:44.750: I/ActivityManager(714): Start proc com.android.phone for content provider com.android.providers.telephony/.TelephonyProvider: pid=999 uid=1001 gids={41001, 3002, 3001, 3003, 1028, 1015, 1004, 2002, 1023}
从上面log中可以看到此次phone进程是由com.android.providers.telephony/.TelephonyProvider叫起,下面排查TelephonyProvider如何叫起Phone进程的。
首先通过查看源码确认可以如下:
com.android.phone"
android:allowClearUserData="https://www.it610.com/article/false"
android:allowBackup="false"
android:label="@string/app_label"
android:icon="@drawable/ic_launcher_phone">
android:authorities="telephony"
android:exported="true"
android:multiprocess="false" />
android:authorities="cb"
android:exported="true"
android:multiprocess="false" />
【深入分析Android开机找网延迟】android:authorities="sms"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS" />
android:authorities="wappush"
android:exported="true"
android:multiprocess="false" />
android:authorities="mms"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS">
android:authorities="mms-sms"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS" />
android:authorities="usersms"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS" />
android:authorities="usercb"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS" />
android:authorities="usermms"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS">
从上面源码来看TelephonyProvider属于com.android.phone进程,可通过调用TelephonyProvider来启动phone进程,因此进一步排查,从下面log来看可以判断出进程id为878的com.mediatek.systemui中ActivityManager调用TelephonyProvider,而使TelephonyProvider Run在phone进程,进而启动了com.andorid.phone进程,也就是说我们通过叫起了TelephonyProvider间接叫起了phone进程,叫起TelephonyProvider的常规方法就是通过“content://telephony",这里要重点关注android:authorities="telephony";
01-30 11:02:44.640: D/SignalClusterView(878): setWifiIndicators, visible=false, strengthIcon=com.mediatek.systemui.ext.IconIdWrapper@426584f0, activityIcon=com.mediatek.systemui.ext.IconIdWrapper@42658508, contentDescription=Wi-Fi 连接已断开
01-30 11:02:44.643: V/PhoneStatusBar(878): carrierlabel for Gemini=android.widget.LinearLayout{42563b40 I.E..... ......I. 0,0-0,0 #7f080013 app:id/carrier_label_gemini} show=true
01-30 11:02:44.658: D/ActivityManager(714): getContentProviderImpl: from callerandroid.app.ApplicationThreadProxy@42ab1f60 (pid=878) to get content provider telephony
01-30 11:02:44.750: I/ActivityManager(714): Start proc com.android.phone for content provider com.android.providers.telephony/.TelephonyProvider: pid=999 uid=1001 gids={41001, 3002, 3001, 3003, 1028, 1015, 1004, 2002, 1023}
从上面log来看是878进程(systemui)叫起了com.android.providers.telephony/.TelephonyProvider(进程id为999),也就是说是systemui叫起了TelephonyProvider,进而叫起了com.android.phone进程。参考log如下:
01-30 11:02:42.133: I/SurfaceFlinger(146): EventThread Client Pid (714) created
因此我们需要排查systemui中的的代码,参考排查方法如下:
01-30 11:02:44.640: D/SignalClusterView(878): setWifiIndicators, visible=false, strengthIcon=com.mediatek.systemui.ext.IconIdWrapper@426584f0, activityIcon=com.mediatek.systemui.ext.IconIdWrapper@42658508, contentDescription=Wi-Fi 连接已断开
01-30 11:02:44.643: V/PhoneStatusBar(878): carrierlabel for Gemini=android.widget.LinearLayout{42563b40 I.E..... ......I. 0,0-0,0 #7f080013 app:id/carrier_label_gemini} show=true
01-30 11:02:44.658: D/ActivityManager(714): getContentProviderImpl: from callerandroid.app.ApplicationThreadProxy@42ab1f60 (pid=878) to get content provider telephony
01-30 11:02:44.750: I/ActivityManager(714): Start proc com.android.phone for content provider com.android.providers.telephony/.TelephonyProvider: pid=999 uid=1001 gids={41001, 3002, 3001, 3003, 1028, 1015, 1004, 2002, 1023}
从上面log来看是878进程(systemui)叫起了com.android.providers.telephony/.TelephonyProvider(进程id为999),也就是说依然是systemui叫起了TelephonyProvider,进而叫起了com.android.phone进程。
然后根据log中来看在叫起TelephonyProvider进程前,上面标红的地方(878为systemui进程)作了操作,下面是源码中的调用关系,从下面源码来看的确有很多地方调用了TelephonyProvider,如HuaweiQuickSettingsController等。需要排查一下这些地方为何调用。
SimInfoManager类中
public static final Uri CONTENT_URI =
Uri.parse("content://telephony/siminfo");
......
public static List getInsertedSimInfoList(Context ctx) {
logd("[getInsertedSimInfoList]+");
ArrayList simList = new ArrayList();
Cursor cursor = ctx.getContentResolver().query(CONTENT_URI,
null, SLOT + "!=" + SLOT_NONE, null, null);
//这里调用了TelephonyProvider
try {
if (cursor != null) {
while (cursor.moveToNext()) {
simList.add(fromCursor(cursor));
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
logd("[getInsertedSimInfoList]- " + simList.size() + " infos return");
return simList;
}
SIMHelper类中
private static List getSortedSIMInfoList(Context context) {
List simInfoList = SimInfoManager.getInsertedSimInfoList(context);
Collections.sort(simInfoList, new Comparator() {
.....
PhoneStatusBar类中
public void showSimIndicator(String businessType) {
if (mIsSimIndicatorShowing) {
hideSimIndicator();
}
mBusinessType = businessType;
long simId = SIMHelper.getDefaultSIM(mContext, businessType);
Xlog.d(TAG, "showSimIndicator, show SIM indicator which business is " + businessType + "simId = "+simId+".");
if (simId == android.provider.Settings.System.DEFAULT_SIM_SETTING_ALWAYS_ASK) {
List simInfos = SIMHelper.getSIMInfoList(mContext);
....
在源码中有很多调用的地方,或许是为了获取sim的状态而调用了TelephonyProvider,具体繁琐的log和代码就不解释了。
到此整个分析过程都在这里了,如有疑问可以发消息给我,一起来讨论。
推荐阅读
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- android防止连续点击的简单实现(kotlin)
- Android|Android install 多个设备时指定设备