前言
淘宝团队找到小米的三方团队来反馈一个问题,说只有在小米手机上会复现无法执行手淘的收藏功能的问题;经过手淘的加log调查,基本确认是bindService无法调起一个叫FavGoodService的服务,需要小米调查下原因;然后三方团队过来找AMS相关的人一起调研下
初步分析
一开始想的是绝对不可能是小米的问题啊,小米如何定制化都不能让一个服务起不来啊;但是淘宝说只有小米反馈了这个问题,因此就只能结合内部日志开看这个问题;
这一看,发现淘宝的服务问题很大啊,不只是FavGoodService这一个服务的问题
03-01 12:49:59.680 1000 1504 3613 W ActivityManager: Unable to start service Intent { act=com.taobao.dai.DAI_SERVICE pkg=com.taobao.taobao } U=0: not found
03-01 12:49:59.869 1000 1504 4477 W ActivityManager: Unable to start service Intent { act=mtopsdk.xstate.aidl.IXState cat=[android.intent.category.DEFAULT] pkg=com.taobao.taobao cmp=com.taobao.taobao/mtopsdk.xstate.XStateService } U=0: not found
03-01 12:50:00.253 1000 1504 4477 W ActivityManager: Unable to start service Intent { act=anetwork.channel.aidl.IRemoteNetworkGetter cat=[android.intent.category.DEFAULT] cmp=com.taobao.taobao/anetwork.channel.aidl.NetworkService } U=0: not found
03-01 12:50:09.663 1000 1504 3584 W ActivityManager: Unable to start service Intent { cmp=com.taobao.taobao/com.alibaba.analytics.AnalyticsService } U=0: not found
03-01 12:50:11.561 1000 1504 6898 W ActivityManager: Unable to start service Intent { act=com.taobao.accs.intent.action.RECEIVE pkg=com.taobao.taobao cmp=com.taobao.taobao/com.taobao.accs.data.MsgDistributeService (has extras) } U=0: not found
那么就需要结合fw代码来看这个问题了
报错的地方在ServiceLookupResult里面
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
ServiceRecord r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
ServiceMap smap = getServiceMapLocked(userId);
final ComponentName comp = service.getComponent();
if (comp != null) {
r = smap.mServicesByName.get(comp);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by component: " + r);
}
if (r == null && !isBindExternal) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
}
if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
&& !callingPackage.equals(r.packageName)) {
// If an external service is running within its own package, other packages
// should not bind to that instance.
r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
}
if (r == null) {
try {
int flags = ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
if (allowInstant) {
flags |= PackageManager.MATCH_INSTANT;
}
// TODO: come back and remove this assumption to triage all services
ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,
resolvedType, flags, userId, callingUid);
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
if (sInfo == null) { //在这里会打出上面的log
Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
": not found");
return null;
}
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
if (isBindExternal) {
if (!sInfo.exported) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not exported");
}
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an isolatedProcess");
}
// Run the service under the calling package's application.
ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
if (aInfo == null) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
"could not resolve client package " + callingPackage);
}
sInfo = new ServiceInfo(sInfo);
sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
sInfo.applicationInfo.packageName = aInfo.packageName;
sInfo.applicationInfo.uid = aInfo.uid;
name = new ComponentName(aInfo.packageName, name.getClassName());
service.setComponent(name);
} else {
throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
name);
}
} else if (isBindExternal) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an externalService");
}
if (userId > 0) {
if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
sInfo.name, sInfo.flags)
&& mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
userId = 0;
smap = getServiceMapLocked(0);
}
sInfo = new ServiceInfo(sInfo);
sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
}
r = smap.mServicesByName.get(name);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE,
"Retrieved via pm by intent: " + r);
if (r == null && createIfNeeded) {
final Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
final BatteryStatsImpl.Uid.Pkg.Serv ss;
final BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
ss = stats.getServiceStatsLocked(
sInfo.applicationInfo.uid, sInfo.packageName,
sInfo.name);
}
r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
res.setService(r);
smap.mServicesByName.put(name, r);
smap.mServicesByIntent.put(filter, r);
// Make sure this component isn't in the pending list.
for (int i=mPendingServices.size()-1; i>=0; i--) {
final ServiceRecord pr = mPendingServices.get(i);
if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid
&& pr.name.equals(name)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Remove pending: " + pr);
mPendingServices.remove(i);
}
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Retrieve created new service: " + r);
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
if (r != null) {
...//下面是非第一次启动的逻辑,与我们这里关系不大
}
}
会打出上面的log,说明第一次启动服务时PMS没有找到对应的服务;但看了下相应的机器,服务明明已经在manifest的服务里面了,那么是怎么回事呢,只能看解析流程;即
ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,
resolvedType, flags, userId, callingUid);
最终会调用到PMS的resolveService
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
}
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(
flags, userId, intent, callingUid, false /*includeInstantApps*/);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
// just arbitrarily pick the first one.
return query.get(0);
}
}
return null;
}
private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
//前面都是一些权限检查,不会有问题的,关键看getServiceInfo
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
// used when either 1) the service is in an instant application and the
// caller is not the same instant application or 2) the calling package is
// ephemeral and the activity is not visible to ephemeral applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(si.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return applyPostServiceResolutionFilter(
mServices.queryIntent(intent, resolvedType, flags, userId),
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return applyPostServiceResolutionFilter(
mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services,
userId),
instantAppPkgName);
}
return Collections.emptyList();
}
}
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) {
return null;
}
return PackageParser.generateServiceInfo(
s, flags, ps.readUserState(userId), userId);
}
}
return null;
}
可以看到关键点在isEnabledAndMatchLPr,如果这里返回false,那么就会直接返回null
然后调用到pm相关数据结构Settings里面的函数isEnabledAndMatchLPr
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
final PackageUserState userState = ps.readUserState(userId);
return userState.isMatch(componentInfo, flags);
}
可以看到userState.isMatch(componentInfo, flags);决定了返回值
PackageUserState
/**
* Test if the given component is considered installed, enabled and a match
* for the given flags.
*
* <p>
* Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and
* {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
* </p>
*/
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;
}
最有可能返回false的地方isEnabled(componentInfo, flags)
/**
* Test if the given component is considered enabled.
*/
public boolean isEnabled(ComponentInfo componentInfo, int flags) {
if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
return true;
}
// First check if the overall package is disabled; if the package is
// enabled then fall through to check specific component
switch (this.enabled) {
case COMPONENT_ENABLED_STATE_DISABLED:
case COMPONENT_ENABLED_STATE_DISABLED_USER:
return false;
case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
return false;
}
case COMPONENT_ENABLED_STATE_DEFAULT:
if (!componentInfo.applicationInfo.enabled) {
return false;
}
case COMPONENT_ENABLED_STATE_ENABLED:
break;
}
// Check if component has explicit state before falling through to
// the manifest default
if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
return true;
}
if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
return false;
}
return componentInfo.enabled;
}
然后我们看一下相应package的dump信息,可以发现
disabledComponents:
com.laiwang.protocol.android.LWPService
com.taobao.activelocation.service.aidl.TBLocationServiceImpl
com.taobao.activelocation.report.service.ActiveReportService
com.taobao.acds.compact.ACDSBusinessService
com.taobao.accs.EventReceiver
com.taobao.taobao.TaobaoIntentService
com.taobao.agoo.AgooCommondReceiver
com.taobao.passivelocation.aidl.PassiveLocationServiceImpl
com.taobao.passivelocation.report.service.LocationReportService
com.taobao.accs.data.MsgDistributeService
com.taobao.wireless.bcportserver.PortServerReceiver
com.laiwang.protocol.android.LwpAccsService
anetwork.channel.aidl.NetworkService
com.xiaomi.push.service.XMPushService
com.alibaba.analytics.AnalyticsService
com.amap.api.location.APSService
com.taobao.favorites.service.FavGoodService
com.taobao.wireless.bcportserver.PortsService
com.taobao.alivfssdk.monitor.AVFSMonitorService
org.android.agoo.accs.AgooService
mtopsdk.xstate.XStateService
com.taobao.passivelocation.gathering.service.LocationGatheringService
com.taobao.orange.service.OrangeApiService
androidx.work.impl.background.systemalarm.SystemAlarmService
com.taobao.accs.internal.AccsJobService
com.taobao.acds.compact.AccsACDSService
com.taobao.accs.ServiceReceiver
enabledComponents:
com.taobao.accs.ChannelService
androidx.work.impl.background.systemjob.SystemJobService
淘宝很多的Service,Receiver组件被disable掉了,所以自然无法找到,也启动不了
直到这里,终于确认了无法启动Service的原因,组件被disable;但是只能到这一步了,因为从log里根本无法看出是什么原因被disable的
问题确认
被disable有三种可能
1.小米安全中心disable
2.用户root
3.淘宝自己disable
问了下安全中心的同事,原来有disable app的功能,后来也删了;且disable app的表现形式不是如上的log,故排除;而用户提供的bugreport里面没有root的属性,因此也不是root导致的;那么只能怀疑淘宝自己调用了相关接口,setComponentEnabledSetting;淘宝查了下,说没有会调这里的地方;至此,似乎没有办法继续查下去了;
感谢三方的同事,看了多份log之后,发现了一个规律;用户bugreport里面的淘宝都是从预装版本上升级上来的,会不会是预装版本的问题?
试一下,点开预装版本,从应用商店升级,果然复现了;知道复现路径就好办了,赶紧调试一下;我们在PMS的setComponentEnabledSetting处加上条件断点(startWith("com.taobao"));奇怪,断不住,难道还有其他接口? 全局查一下代码,不会有其他接口;那么怎么会断不住呢;
经过多次复现,找规律,终于找到了复现路径;从预装版本一点开就会disable,挂上了,调试结果如图:
taobao_调试结果1.png taobao_调试结果2.png taobao_调试结果3.png taobao_调试结果4.png对端进程是淘宝,还是淘宝调用的;淘宝的调用栈(kill -3)是
taobao_调试结果5.png果然与预装应用有关;
猜测一种情况是这样的,在预装版本上有这样一个逻辑,点淘宝时先将一些Service disable;然后用户点同意的时候再重新enable;但是有可能用户点开了第一个界面,没有点同步,直接回到桌面;然后后面想起来用的时候从应用商店升级到最新版本,但是该版本没有预装版本的逻辑了,所以用户没有点同意的机会了;disable的服务就一直保留了下来;复现条件比较苛刻,这也解释了概率不高的原因;
经过和淘宝的再次沟通,果然如此,这是淘宝正式版本和预装版本的逻辑不能完美自恰导致的问题;淘宝团队反映会在下一个版本修复;也总算为小米甩出一个锅;
总结
1.原来没有意识到disableComponents会在Service的查找启动阶段就会起作用,以后又了解多了一种服务起不来的可能性;
2.这个问题本身原因不复杂,但调查过程有点曲折,且不属于小米定制化错误导致的问题,因此记录下;
网友评论