bindService相关-一个淘宝无法执行收藏功能的问题

2019-03-01  本文已影响0人  weiinter105

前言

淘宝团队找到小米的三方团队来反馈一个问题,说只有在小米手机上会复现无法执行手淘的收藏功能的问题;经过手淘的加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.这个问题本身原因不复杂,但调查过程有点曲折,且不属于小米定制化错误导致的问题,因此记录下;

上一篇下一篇

猜你喜欢

热点阅读