Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54d82637 authored by Winson's avatar Winson
Browse files

Handle general and specific cross profile logic

Previous resolve had a weird behavior where a specific IntentFilter
configured cross profile filter configuration (as opposed to a general
configuration requiring allow_parent_profile_app_linking to be toggled)
would be added into the candidate set, which the code assumed was only
for the user ID being queried.

Instead, this specific cross profile resolution needs to be kept
separate as a different user, and applied if when the general
resolution is unavailable.

This makes both specific and general branchs assign the same
CrossProfileDomainInfo that domain verification was already checking,
and so should allow the logic to work in cases where the specific info
was previously dropped.

Bug: 189222753

Test: CtsDomainVerificationDeviceMultiUserTestCases

Change-Id: I0ac12d7125a7c5a9d4b9b35692d13928cbeb84e3
parent 320bc8ff
Loading
Loading
Loading
Loading
+4 −1
Original line number Original line Diff line number Diff line
@@ -9,7 +9,10 @@
      ]
      ]
    },
    },
    {
    {
      "name": "CtsDomainVerificationDeviceTestCases"
      "name": "CtsDomainVerificationDeviceStandaloneTestCases"
    },
    {
      "name": "CtsDomainVerificationDeviceMultiUserTestCases"
    },
    },
    {
    {
      "name": "CtsDomainVerificationHostTestCases"
      "name": "CtsDomainVerificationHostTestCases"
+162 −90
Original line number Original line Diff line number Diff line
@@ -2332,11 +2332,11 @@ public class PackageManagerService extends IPackageManager.Stub
                List<CrossProfileIntentFilter> matchingFilters =
                List<CrossProfileIntentFilter> matchingFilters =
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
                // Check for results that need to skip the current profile.
                // Check for results that need to skip the current profile.
                ResolveInfo xpResolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
                ResolveInfo skipProfileInfo  = querySkipCurrentProfileIntents(matchingFilters,
                        resolvedType, flags, userId);
                        intent, resolvedType, flags, userId);
                if (xpResolveInfo != null) {
                if (skipProfileInfo != null) {
                    List<ResolveInfo> xpResult = new ArrayList<>(1);
                    List<ResolveInfo> xpResult = new ArrayList<>(1);
                    xpResult.add(xpResolveInfo);
                    xpResult.add(skipProfileInfo);
                    return new QueryIntentActivitiesResult(
                    return new QueryIntentActivitiesResult(
                            applyPostResolutionFilter(
                            applyPostResolutionFilter(
                                    filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
                                    filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
@@ -2351,54 +2351,55 @@ public class PackageManagerService extends IPackageManager.Stub
                        false /*skipPackageCheck*/, flags);
                        false /*skipPackageCheck*/, flags);
                // Check for cross profile results.
                // Check for cross profile results.
                boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
                boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
                xpResolveInfo = queryCrossProfileIntents(
                CrossProfileDomainInfo specificXpInfo = queryCrossProfileIntents(
                        matchingFilters, intent, resolvedType, flags, userId,
                        matchingFilters, intent, resolvedType, flags, userId,
                        hasNonNegativePriorityResult);
                        hasNonNegativePriorityResult);
                if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
                    boolean isVisibleToUser = filterIfNotSystemUser(
                            Collections.singletonList(xpResolveInfo), userId).size() > 0;
                    if (isVisibleToUser) {
                        result.add(xpResolveInfo);
                        sortResult = true;
                    }
                }
                if (intent.hasWebURI()) {
                if (intent.hasWebURI()) {
                    CrossProfileDomainInfo xpDomainInfo = null;
                    CrossProfileDomainInfo generalXpInfo = null;
                    final UserInfo parent = getProfileParent(userId);
                    final UserInfo parent = getProfileParent(userId);
                    if (parent != null) {
                    if (parent != null) {
                        xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
                        generalXpInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
                                flags, userId, parent.id);
                                flags, userId, parent.id);
                    }
                    }
                    if (xpDomainInfo != null) {
                        if (xpResolveInfo != null) {
                    // Generalized cross profile intents take precedence over specific.
                            // If we didn't remove it, the cross-profile ResolveInfo would be twice
                    // Note that this is the opposite of the intuitive order.
                            // in the result.
                    CrossProfileDomainInfo prioritizedXpInfo =
                            result.remove(xpResolveInfo);
                            generalXpInfo != null ? generalXpInfo : specificXpInfo;
                        }
                        if (result.size() == 0 && !addInstant) {
                    if (!addInstant) {
                        if (result.isEmpty() && prioritizedXpInfo != null) {
                            // No result in current profile, but found candidate in parent user.
                            // No result in current profile, but found candidate in parent user.
                            // And we are not going to add ephemeral app, so we can return the
                            // And we are not going to add ephemeral app, so we can return the
                            // result straight away.
                            // result straight away.
                            result.add(xpDomainInfo.resolveInfo);
                            result.add(prioritizedXpInfo.resolveInfo);
                            return new QueryIntentActivitiesResult(
                            return new QueryIntentActivitiesResult(
                                    applyPostResolutionFilter(result, instantAppPkgName,
                                    applyPostResolutionFilter(result, instantAppPkgName,
                                            allowDynamicSplits, filterCallingUid, resolveForStart,
                                            allowDynamicSplits, filterCallingUid, resolveForStart,
                                            userId, intent));
                                            userId, intent));
                        }
                        } else if (result.size() <= 1 && prioritizedXpInfo == null) {
                    } else if (result.size() <= 1 && !addInstant) {
                            // No result in parent user and <= 1 result in current profile, and we
                            // No result in parent user and <= 1 result in current profile, and we
                        // are not going to add ephemeral app, so we can return the result without
                            // are not going to add ephemeral app, so we can return the result
                        // further processing.
                            // without further processing.
                            return new QueryIntentActivitiesResult(
                            return new QueryIntentActivitiesResult(
                                    applyPostResolutionFilter(result, instantAppPkgName,
                                    applyPostResolutionFilter(result, instantAppPkgName,
                                allowDynamicSplits, filterCallingUid, resolveForStart, userId,
                                            allowDynamicSplits, filterCallingUid, resolveForStart,
                                intent));
                                            userId, intent));
                        }
                        }
                    }
                    // We have more than one candidate (combining results from current and parent
                    // We have more than one candidate (combining results from current and parent
                    // profile), so we need filtering and sorting.
                    // profile), so we need filtering and sorting.
                    result = filterCandidatesWithDomainPreferredActivitiesLPr(
                    result = filterCandidatesWithDomainPreferredActivitiesLPr(
                            intent, flags, result, xpDomainInfo, userId);
                            intent, flags, result, prioritizedXpInfo, userId);
                    sortResult = true;
                    sortResult = true;
                } else {
                    // If not web Intent, just add result to candidate set and let ResolverActivity
                    // figure it out.
                    if (specificXpInfo != null) {
                        result.add(specificXpInfo.resolveInfo);
                        sortResult = true;
                    }
                }
                }
            } else {
            } else {
                final PackageSetting setting =
                final PackageSetting setting =
@@ -2830,15 +2831,17 @@ public class PackageManagerService extends IPackageManager.Stub
                if (ps == null) {
                if (ps == null) {
                    continue;
                    continue;
                }
                }
                int approvalLevel = mDomainVerificationManager
                        .approvalLevelForDomain(ps, intent, flags, parentUserId);
                if (result == null) {
                if (result == null) {
                    result = new CrossProfileDomainInfo();
                    result = new CrossProfileDomainInfo(createForwardingResolveInfoUnchecked(
                    result.resolveInfo = createForwardingResolveInfoUnchecked(
                            new WatchedIntentFilter(), sourceUserId, parentUserId), approvalLevel);
                            new WatchedIntentFilter(), sourceUserId, parentUserId);
                } else {
                    result.highestApprovalLevel =
                            Math.max(approvalLevel, result.highestApprovalLevel);
                }
                }
                result.highestApprovalLevel = Math.max(mDomainVerificationManager
                        .approvalLevelForDomain(ps, intent, resultTargetUser, flags,
                                parentUserId), result.highestApprovalLevel);
            }
            }
            if (result != null && result.highestApprovalLevel
            if (result != null && result.highestApprovalLevel
                    <= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
                    <= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
@@ -3085,8 +3088,8 @@ public class PackageManagerService extends IPackageManager.Stub
                    final String packageName = info.activityInfo.packageName;
                    final String packageName = info.activityInfo.packageName;
                    final PackageSetting ps = mSettings.getPackageLPr(packageName);
                    final PackageSetting ps = mSettings.getPackageLPr(packageName);
                    if (ps.getInstantApp(userId)) {
                    if (ps.getInstantApp(userId)) {
                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
                                instantApps, flags, userId)) {
                                userId)) {
                            if (DEBUG_INSTANT) {
                            if (DEBUG_INSTANT) {
                                Slog.v(TAG, "Instant app approved for intent; pkg: "
                                Slog.v(TAG, "Instant app approved for intent; pkg: "
                                        + packageName);
                                        + packageName);
@@ -3413,30 +3416,61 @@ public class PackageManagerService extends IPackageManager.Stub
        }
        }
        /**
        /**
         * If the filter's target user can handle the intent and is enabled: returns a ResolveInfo
         * If the filter's target user can handle the intent and is enabled: a [ResolveInfo] that
         * that
         * will forward the intent to the filter's target user, along with the highest approval of
         * will forward the intent to the filter's target user.
         * any handler in the target user. Otherwise, returns null.
         * Otherwise, returns null.
         */
         */
        private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter,
        @Nullable
                Intent intent,
        private CrossProfileDomainInfo createForwardingResolveInfo(
                String resolvedType, int flags, int sourceUserId) {
                @NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
                @Nullable String resolvedType, int flags, int sourceUserId) {
            int targetUserId = filter.getTargetUserId();
            int targetUserId = filter.getTargetUserId();
            if (!isUserEnabled(targetUserId)) {
                return null;
            }
            List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
            List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
                    resolvedType, flags, targetUserId);
                    resolvedType, flags, targetUserId);
            if (resultTargetUser != null && isUserEnabled(targetUserId)) {
            if (CollectionUtils.isEmpty(resultTargetUser)) {
                // If all the matches in the target profile are suspended, return null.
                return null;
            }
            ResolveInfo forwardingInfo = null;
            for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
            for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
                    if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
                ResolveInfo targetUserResolveInfo = resultTargetUser.get(i);
                if ((targetUserResolveInfo.activityInfo.applicationInfo.flags
                        & ApplicationInfo.FLAG_SUSPENDED) == 0) {
                        & ApplicationInfo.FLAG_SUSPENDED) == 0) {
                        return createForwardingResolveInfoUnchecked(filter,
                    forwardingInfo = createForwardingResolveInfoUnchecked(filter, sourceUserId,
                              sourceUserId, targetUserId);
                            targetUserId);
                    }
                    break;
                }
                }
            }
            }
            if (forwardingInfo == null) {
                // If all the matches in the target profile are suspended, return null.
                return null;
                return null;
            }
            }
            int highestApprovalLevel = DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
            int size = resultTargetUser.size();
            for (int i = 0; i < size; i++) {
                ResolveInfo riTargetUser = resultTargetUser.get(i);
                if (riTargetUser.handleAllWebDataURI) {
                    continue;
                }
                String packageName = riTargetUser.activityInfo.packageName;
                PackageSetting ps = mSettings.getPackageLPr(packageName);
                if (ps == null) {
                    continue;
                }
                highestApprovalLevel = Math.max(highestApprovalLevel, mDomainVerificationManager
                                .approvalLevelForDomain(ps, intent, flags, targetUserId));
            }
            return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel);
        }
        public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
        public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
                int sourceUserId, int targetUserId) {
                int sourceUserId, int targetUserId) {
            ResolveInfo forwardingResolveInfo = new ResolveInfo();
            ResolveInfo forwardingResolveInfo = new ResolveInfo();
@@ -3473,14 +3507,20 @@ public class PackageManagerService extends IPackageManager.Stub
        }
        }
        // Return matching ResolveInfo in target user if any.
        // Return matching ResolveInfo in target user if any.
        private ResolveInfo queryCrossProfileIntents(
        @Nullable
        private CrossProfileDomainInfo queryCrossProfileIntents(
                List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
                List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
                int flags, int sourceUserId, boolean matchInCurrentProfile) {
                int flags, int sourceUserId, boolean matchInCurrentProfile) {
            if (matchingFilters != null) {
            if (matchingFilters == null) {
                return null;
            }
            // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
            // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
            // match the same intent. For performance reasons, it is better not to
            // match the same intent. For performance reasons, it is better not to
            // run queryIntent twice for the same userId
            // run queryIntent twice for the same userId
            SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
            SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
            CrossProfileDomainInfo resultInfo = null;
            int size = matchingFilters.size();
            int size = matchingFilters.size();
            for (int i = 0; i < size; i++) {
            for (int i = 0; i < size; i++) {
                CrossProfileIntentFilter filter = matchingFilters.get(i);
                CrossProfileIntentFilter filter = matchingFilters.get(i);
@@ -3493,16 +3533,35 @@ public class PackageManagerService extends IPackageManager.Stub
                        && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
                        && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
                    // Checking if there are activities in the target user that can handle the
                    // Checking if there are activities in the target user that can handle the
                    // intent.
                    // intent.
                        ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
                    CrossProfileDomainInfo info = createForwardingResolveInfo(filter, intent,
                            resolvedType, flags, sourceUserId);
                            resolvedType, flags, sourceUserId);
                        if (resolveInfo != null) return resolveInfo;
                    if (info != null) {
                        resultInfo = info;
                        break;
                    }
                    alreadyTriedUserIds.put(targetUserId, true);
                    alreadyTriedUserIds.put(targetUserId, true);
                }
                }
            }
            }
            if (resultInfo == null) {
                return null;
            }
            ResolveInfo forwardingResolveInfo = resultInfo.resolveInfo;
            if (!isUserEnabled(forwardingResolveInfo.targetUserId)) {
                return null;
            }
            }
            List<ResolveInfo> filteredResult =
                    filterIfNotSystemUser(Collections.singletonList(forwardingResolveInfo),
                            sourceUserId);
            if (filteredResult.isEmpty()) {
                return null;
                return null;
            }
            }
            return resultInfo;
        }
        private ResolveInfo querySkipCurrentProfileIntents(
        private ResolveInfo querySkipCurrentProfileIntents(
                List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
                List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
                int flags, int sourceUserId) {
                int flags, int sourceUserId) {
@@ -3513,10 +3572,10 @@ public class PackageManagerService extends IPackageManager.Stub
                    if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
                    if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
                        // Checking if there are activities in the target user that can handle the
                        // Checking if there are activities in the target user that can handle the
                        // intent.
                        // intent.
                        ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
                        CrossProfileDomainInfo info = createForwardingResolveInfo(filter, intent,
                                resolvedType, flags, sourceUserId);
                                resolvedType, flags, sourceUserId);
                        if (resolveInfo != null) {
                        if (info != null) {
                            return resolveInfo;
                            return info.resolveInfo;
                        }
                        }
                    }
                    }
                }
                }
@@ -4026,8 +4085,8 @@ public class PackageManagerService extends IPackageManager.Stub
                if (ps != null) {
                if (ps != null) {
                    // only check domain verification status if the app is not a browser
                    // only check domain verification status if the app is not a browser
                    if (!info.handleAllWebDataURI) {
                    if (!info.handleAllWebDataURI) {
                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
                                resolvedActivities, flags, userId)) {
                                userId)) {
                            if (DEBUG_INSTANT) {
                            if (DEBUG_INSTANT) {
                                Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
                                Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
                                        + ", approved");
                                        + ", approved");
@@ -9525,7 +9584,7 @@ public class PackageManagerService extends IPackageManager.Stub
                        final String packageName = ri.activityInfo.packageName;
                        final String packageName = ri.activityInfo.packageName;
                        final PackageSetting ps = mSettings.getPackageLPr(packageName);
                        final PackageSetting ps = mSettings.getPackageLPr(packageName);
                        if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
                        if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
                                intent, query, flags, userId)) {
                                intent, flags, userId)) {
                            return ri;
                            return ri;
                        }
                        }
                    }
                    }
@@ -9582,10 +9641,10 @@ public class PackageManagerService extends IPackageManager.Stub
     */
     */
    private static boolean hasAnyDomainApproval(
    private static boolean hasAnyDomainApproval(
            @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
            @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
            @NonNull Intent intent, @NonNull List<ResolveInfo> candidates,
            @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
            @UserIdInt int userId) {
        return manager.approvalLevelForDomain(pkgSetting, intent, candidates, resolveInfoFlags,
        return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
                userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
                > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
    }
    }
    /**
    /**
@@ -10018,7 +10077,20 @@ public class PackageManagerService extends IPackageManager.Stub
    private static class CrossProfileDomainInfo {
    private static class CrossProfileDomainInfo {
        /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
        /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
        ResolveInfo resolveInfo;
        ResolveInfo resolveInfo;
        int highestApprovalLevel = DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
        int highestApprovalLevel;
        CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel) {
            this.resolveInfo = resolveInfo;
            this.highestApprovalLevel = highestApprovalLevel;
        }
        @Override
        public String toString() {
            return "CrossProfileDomainInfo{"
                    + "resolveInfo=" + resolveInfo
                    + ", highestApprovalLevel=" + highestApprovalLevel
                    + '}';
        }
    }
    }
    private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
    private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
+0 −1
Original line number Original line Diff line number Diff line
@@ -389,7 +389,6 @@ public interface DomainVerificationManagerInternal {
     */
     */
    @ApprovalLevel
    @ApprovalLevel
    int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
    int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
            @NonNull List<ResolveInfo> candidates,
            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);


    /**
    /**
+19 −3
Original line number Original line Diff line number Diff line
@@ -1717,7 +1717,6 @@ public class DomainVerificationService extends SystemService


    @Override
    @Override
    public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
    public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
            @NonNull List<ResolveInfo> candidates,
            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
        String packageName = pkgSetting.getName();
        String packageName = pkgSetting.getName();
        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
@@ -1783,9 +1782,26 @@ public class DomainVerificationService extends SystemService
            return APPROVAL_LEVEL_NONE;
            return APPROVAL_LEVEL_NONE;
        }
        }


        if (!pkgUserState.installed || !pkgUserState.isPackageEnabled(pkg)) {
        if (!pkgUserState.installed) {
            if (DEBUG_APPROVAL) {
                debugApproval(packageName, debugObject, userId, false,
                        "package not installed for user");
            }
            return APPROVAL_LEVEL_NONE;
        }

        if (!pkgUserState.isPackageEnabled(pkg)) {
            if (DEBUG_APPROVAL) {
            if (DEBUG_APPROVAL) {
                debugApproval(packageName, debugObject, userId, false, "package not enabled");
                debugApproval(packageName, debugObject, userId, false,
                        "package not enabled for user");
            }
            return APPROVAL_LEVEL_NONE;
        }

        if (pkgUserState.suspended) {
            if (DEBUG_APPROVAL) {
                debugApproval(packageName, debugObject, userId, false,
                        "package suspended for user");
            }
            }
            return APPROVAL_LEVEL_NONE;
            return APPROVAL_LEVEL_NONE;
        }
        }
+4 −1
Original line number Original line Diff line number Diff line
@@ -9,7 +9,10 @@
      ]
      ]
    },
    },
    {
    {
      "name": "CtsDomainVerificationDeviceTestCases"
      "name": "CtsDomainVerificationDeviceStandaloneTestCases"
    },
    {
      "name": "CtsDomainVerificationDeviceMultiUserTestCases"
    },
    },
    {
    {
      "name": "CtsDomainVerificationHostTestCases"
      "name": "CtsDomainVerificationHostTestCases"