美文网首页
bindService相关-一个淘宝无法执行收藏功能的问题

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

作者: weiinter105 | 来源:发表于2019-03-01 17:45 被阅读0次

    前言

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/hghjuqtx.html