Fix component query/binding for multiuser

This change apples to non-UI incall services and carmode-UI service.
When a managed profile user make a calls, we query non-UI incall
services for the managed user AND for the parent user.

We also store the parent handle for the later phase of actual binding.

Similar change is made for carmode-UI. If we dont find a carmode-UI
component for a manager user, we also check using the parent user.

Bug:285988874
Test: atest InCallControllerTests and manual whatsapp workprofile call
Test: workprofile regression tests and manual call tests
Change-Id: I6308db89045d73857900b25eaa422afdc108c7ee
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index a952fba..c44227d 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -47,6 +47,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.permission.PermissionManager;
 import android.telecom.CallAudioState;
 import android.telecom.CallEndpoint;
 import android.telecom.ConnectionService;
@@ -146,16 +147,20 @@
         private long mBindingStartTime;
         private long mDisconnectTime;
 
+        private boolean mHasCrossUserOrProfilePerm;
+
         public InCallServiceInfo(ComponentName componentName,
                 boolean isExternalCallsSupported,
                 boolean isSelfManageCallsSupported,
-                int type) {
+                int type, boolean hasCrossUserOrProfilePerm) {
             mComponentName = componentName;
             mIsExternalCallsSupported = isExternalCallsSupported;
             mIsSelfManagedCallsSupported = isSelfManageCallsSupported;
             mType = type;
+            mHasCrossUserOrProfilePerm = hasCrossUserOrProfilePerm;
         }
 
+        public boolean hasCrossUserOrProfilePermission() { return mHasCrossUserOrProfilePerm; }
         public ComponentName getComponentName() {
             return mComponentName;
         }
@@ -292,8 +297,19 @@
         private boolean mIsNullBinding = false;
         private NotificationManager mNotificationManager;
 
+        //this is really used for cases where the userhandle for a call
+        //does not match what we want to use for bindAsUser
+        private final UserHandle mUserHandleToUseForBinding;
+
         public InCallServiceBindingConnection(InCallServiceInfo info) {
             mInCallServiceInfo = info;
+            mUserHandleToUseForBinding = null;
+        }
+
+        public InCallServiceBindingConnection(InCallServiceInfo info,
+                UserHandle userHandleToUseForBinding) {
+            mInCallServiceInfo = info;
+            mUserHandleToUseForBinding = userHandleToUseForBinding;
         }
 
         @Override
@@ -335,14 +351,31 @@
             Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
             mIsConnected = true;
             mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
-            UserHandle userToBind = getUserFromCall(call);
-            boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userToBind);
+            boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userFromCall);
             // Note that UserHandle.CURRENT fails to capture the work profile, so we need to handle
             // it separately to ensure that the ICS is bound to the appropriate user. If ECBM is
             // active, we know that a work sim was previously used to place a MO emergency call. We
             // need to ensure that we bind to the CURRENT_USER in this case, as the work user would
             // not be running (handled in getUserFromCall).
-            userToBind = isManagedProfile ? userToBind : UserHandle.CURRENT;
+            UserHandle userToBind = isManagedProfile ? userFromCall : UserHandle.CURRENT;
+            if ((mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_NON_UI
+                    || mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_CAR_MODE_UI) && (
+                    mUserHandleToUseForBinding != null)) {
+                //guarding change for non-UI/carmode-UI services which may not be present for
+                // work profile.
+                //In this case, we use the parent user handle. (This also seems to be more
+                // accurate that USER_CURRENT since we queried/discovered the packages using the
+                // parent handle)
+                if (mInCallServiceInfo.hasCrossUserOrProfilePermission()) {
+                    userToBind = mUserHandleToUseForBinding;
+                } else {
+                    Log.i(this,
+                            "service does not have INTERACT_ACROSS_PROFILES or "
+                                    + "INTERACT_ACROSS_USERS permission");
+                }
+            }
+            Log.i(this, "using user id: %s for binding. User from Call is: %s", userToBind,
+                    userFromCall);
             if (!mContext.bindServiceAsUser(intent, mServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
@@ -668,7 +701,7 @@
                 }
 
                 mCarModeConnection = mCurrentConnection =
-                        new InCallServiceBindingConnection(carModeConnectionInfo);
+                        new InCallServiceBindingConnection(carModeConnectionInfo, userHandle);
                 mIsCarMode = true;
 
                 int result = mCurrentConnection.connect(null);
@@ -960,34 +993,137 @@
         }
     };
 
+    private UserHandle findChildManagedProfileUser(UserHandle parent, UserManager um) {
+        UserHandle childManagedProfileUser = null;
+
+        //find child managed profile user (if any)
+        List<UserHandle> allUsers = um.getAllProfiles();
+        for (UserHandle u : allUsers) {
+            if ((um.getProfileParent(u) != null) && (um.getProfileParent(u).equals(parent))
+                    && um.isManagedProfile(u.getIdentifier())) {
+                //found managed profile child
+                Log.i(this,
+                        "Child managed profile user found: " + u.getIdentifier());
+                childManagedProfileUser = u;
+                break;
+            }
+        }
+        return childManagedProfileUser;
+    }
     private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
+        private List<InCallController.InCallServiceInfo> getNonUiInCallServiceInfoList(
+                Intent intent, UserHandle userHandle) {
+            String changedPackage = intent.getData().getSchemeSpecificPart();
+            List<InCallController.InCallServiceInfo> inCallServiceInfoList =
+                    Arrays.stream(intent.getStringArrayExtra(
+                                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
+                            .map((className) ->
+                                    ComponentName.createRelative(changedPackage,
+                                            className))
+                            .filter(mKnownNonUiInCallServices::contains)
+                            .flatMap(componentName -> getInCallServiceComponents(
+                                    userHandle, componentName,
+                                    IN_CALL_SERVICE_TYPE_NON_UI).stream())
+                            .collect(Collectors.toList());
+            return ((inCallServiceInfoList != null) ? inCallServiceInfoList : new ArrayList<>());
+        }
+
+        //Here we query components using the userHandle. We then also query components using the
+        //parent userHandle (if any) while removing duplicates. For non-dup components found using
+        //parent userHandle, we use the overloaded InCallServiceBindingConnection constructor.
+        @SuppressWarnings("ReturnValueIgnored")
+        private List<InCallServiceBindingConnection> getNonUiInCallServiceBindingConnectionList(
+                Intent intent, @NonNull UserHandle userHandle, UserHandle parentUserHandle) {
+            List<InCallServiceBindingConnection> result = new ArrayList<>();
+            List<InCallController.InCallServiceInfo> serviceInfoListForParent = new ArrayList<>();
+
+            //query and add components for the child
+            List<InCallController.InCallServiceInfo> serviceInfoListForUser =
+                    getNonUiInCallServiceInfoList(intent, userHandle);
+
+            //if user has a parent, get components for parents
+            if (parentUserHandle != null) {
+                serviceInfoListForParent = getNonUiInCallServiceInfoList(intent, parentUserHandle);
+            }
+
+            serviceInfoListForUser
+                    .stream()
+                    .map(InCallServiceBindingConnection::new)
+                    .collect(Collectors.toCollection(() -> result));
+
+            serviceInfoListForParent
+                    .stream()
+                    .filter((e) -> !(serviceInfoListForUser.contains(e)))
+                    .map((serviceinfo) -> new InCallServiceBindingConnection(serviceinfo,
+                            parentUserHandle))
+                    .collect(Collectors.toCollection(() -> result));
+
+            return result;
+        }
+
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.startSession("ICC.pCR");
+            UserManager um = mContext.getSystemService(UserManager.class);
             try {
                 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
                     synchronized (mLock) {
-                        String changedPackage = intent.getData().getSchemeSpecificPart();
                         int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
                         UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
-                        List<InCallServiceBindingConnection> componentsToBind =
-                                Arrays.stream(intent.getStringArrayExtra(
-                                        Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
-                                        .map((className) ->
-                                                ComponentName.createRelative(changedPackage,
-                                                        className))
-                                        .filter(mKnownNonUiInCallServices::contains)
-                                        .flatMap(componentName -> getInCallServiceComponents(
-                                                userHandle, componentName,
-                                                IN_CALL_SERVICE_TYPE_NON_UI).stream())
-                                        .map(InCallServiceBindingConnection::new)
-                                        .collect(Collectors.toList());
+                        boolean isManagedProfile = um.isManagedProfile(userHandle.getIdentifier());
 
-                        if (mNonUIInCallServiceConnections.containsKey(userHandle)) {
-                            mNonUIInCallServiceConnections.get(userHandle).
-                                    addConnections(componentsToBind);
+                        /*
+                        There are two possibilities here:
+                         1) We get a work-profile/managed userHandle. In this case we need to check
+                         if there are any ongoing calls for that user. If yes, then process further
+                         by querying component using this user handle (also bindAsUser using this
+                          handle). Else safely ignore it.
+                                OR
+                         2) We get the primary/non-managed userHandle. In this case, we have two
+                          sub-cases to handle:
+                                   a) If there are ongoing calls for this user, query components
+                                   using this user and addConnections
+                                   b) If there are ongoing calls for the child of this user, we
+                                   also addConnections to that child (but invoke bindAsUser later
+                                    with the parent handle).
+
+                         */
+
+                        UserHandle childManagedProfileUser = findChildManagedProfileUser(
+                                userHandle, um);
+                        boolean isUserKeyPresent = mNonUIInCallServiceConnections.containsKey(
+                                userHandle);
+                        boolean isChildUserKeyPresent = (childManagedProfileUser == null) ? false
+                                : mNonUIInCallServiceConnections.containsKey(
+                                        childManagedProfileUser);
+                        List<InCallServiceBindingConnection> componentsToBindForUser = null;
+                        List<InCallServiceBindingConnection> componentsToBindForChild = null;
+
+                        if(isUserKeyPresent) {
+                            componentsToBindForUser =
+                                    getNonUiInCallServiceBindingConnectionList(intent,
+                                            userHandle, null);
+                        }
+                        if (isChildUserKeyPresent) {
+                            componentsToBindForChild =
+                                    getNonUiInCallServiceBindingConnectionList(intent,
+                                            childManagedProfileUser, userHandle);
                         }
 
+                        Log.i(this,
+                                "isUserKeyPresent:%b isChildKeyPresent:%b isManagedProfile:%b "
+                                        + "user:%d",
+                                isUserKeyPresent, isChildUserKeyPresent, isManagedProfile,
+                                userHandle.getIdentifier());
+
+                        if (isUserKeyPresent && componentsToBindForUser != null) {
+                            mNonUIInCallServiceConnections.get(userHandle).
+                                    addConnections(componentsToBindForUser);
+                        }
+                        if (isChildUserKeyPresent && componentsToBindForChild != null) {
+                            mNonUIInCallServiceConnections.get(childManagedProfileUser).
+                                    addConnections(componentsToBindForChild);
+                        }
                         // If the current car mode app become enabled from disabled, update
                         // the connection to binding
                         updateCarModeForConnections();
@@ -1691,6 +1827,14 @@
     @VisibleForTesting
     public void bindToServices(Call call) {
         UserHandle userFromCall = getUserFromCall(call);
+        UserHandle parentUser = null;
+        UserManager um = mContext.getSystemService(UserManager.class);
+
+        if (um.isManagedProfile(userFromCall.getIdentifier())) {
+            parentUser = um.getProfileParent(userFromCall);
+            Log.i(this, "child:%s  parent:%s", userFromCall, parentUser);
+        }
+
         if (!mInCallServiceConnections.containsKey(userFromCall)) {
             InCallServiceConnection dialerInCall = null;
             InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent(userFromCall);
@@ -1710,10 +1854,24 @@
 
             InCallServiceConnection carModeInCall = null;
             InCallServiceInfo carModeComponentInfo = getCurrentCarModeComponent(userFromCall);
+            InCallServiceInfo carModeComponentInfoForParentUser = null;
+            if(parentUser != null) {
+                //query using parent user too
+                carModeComponentInfoForParentUser = getCurrentCarModeComponent(
+                        parentUser);
+            }
+
             if (carModeComponentInfo != null &&
                     !carModeComponentInfo.getComponentName().equals(
                             mDefaultDialerCache.getSystemDialerComponent())) {
                 carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
+            } else if (carModeComponentInfo == null &&
+                    carModeComponentInfoForParentUser != null &&
+                    !carModeComponentInfoForParentUser.getComponentName().equals(
+                            mDefaultDialerCache.getSystemDialerComponent())) {
+                carModeInCall = new InCallServiceBindingConnection(
+                        carModeComponentInfoForParentUser, parentUser);
+                Log.i(this, "Using car mode component queried using parent handle");
             }
 
             mInCallServiceConnections.put(userFromCall,
@@ -1747,12 +1905,43 @@
 
     private void updateNonUiInCallServices(Call call) {
         UserHandle userFromCall = getUserFromCall(call);
+        UserHandle parentUser = null;
+
+        UserManager um = mContext.getSystemService(UserManager.class);
+        if(um.isManagedProfile(userFromCall.getIdentifier()))
+        {
+            parentUser = um.getProfileParent(userFromCall);
+        }
+
         List<InCallServiceInfo> nonUIInCallComponents =
                 getInCallServiceComponents(userFromCall, IN_CALL_SERVICE_TYPE_NON_UI);
+        List<InCallServiceInfo> nonUIInCallComponentsForParent = new ArrayList<>();
+        if(parentUser != null)
+        {
+            //also get Non-UI services using parent handle.
+            nonUIInCallComponentsForParent =
+                    getInCallServiceComponents(parentUser, IN_CALL_SERVICE_TYPE_NON_UI);
+
+        }
         List<InCallServiceBindingConnection> nonUIInCalls = new LinkedList<>();
         for (InCallServiceInfo serviceInfo : nonUIInCallComponents) {
             nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo));
         }
+
+        //add nonUI InCall services queried using parent user (if any)
+        for (InCallServiceInfo serviceInfo : nonUIInCallComponentsForParent) {
+            if (nonUIInCallComponents.contains(serviceInfo)) {
+                //skip dups
+                Log.i(this, "skipped duplicate component found using parent user: "
+                        + serviceInfo.getComponentName());
+            } else {
+                nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo, parentUser));
+                Log.i(this,
+                        "added component queried using parent user: "
+                                + serviceInfo.getComponentName());
+            }
+        }
+
         List<String> callCompanionApps = mCallsManager
                 .getRoleManagerAdapter().getCallCompanionApps();
         if (callCompanionApps != null && !callCompanionApps.isEmpty()) {
@@ -1819,7 +2008,7 @@
             // Last Resort: Try to bind to the ComponentName given directly.
             Log.e(this, new Exception(), "Package Manager could not find ComponentName: "
                     + componentName + ". Trying to bind anyway.");
-            return new InCallServiceInfo(componentName, false, false, type);
+            return new InCallServiceInfo(componentName, false, false, type, false);
         }
     }
 
@@ -1854,6 +2043,34 @@
         return getInCallServiceComponents(userHandle, packageName,
                 componentName, requestedType, true /* ignoreDisabled */);
     }
+    private boolean canInteractAcrossUsersOrProfiles(ServiceInfo serviceInfo,
+            PackageManager packageManager) {
+        String op = AppOpsManager.permissionToOp("android.permission.INTERACT_ACROSS_PROFILES");
+        String[] uidPackages = packageManager.getPackagesForUid(serviceInfo.applicationInfo.uid);
+
+        boolean hasInteractAcrossProfiles = Arrays.stream(uidPackages).anyMatch(
+                p -> ((packageManager.checkPermission(
+                        Manifest.permission.INTERACT_ACROSS_PROFILES,
+                        p) == PackageManager.PERMISSION_GRANTED)
+                ));
+        boolean hasInteractAcrossUsers = Arrays.stream(uidPackages).anyMatch(
+                p -> ((packageManager.checkPermission(
+                        Manifest.permission.INTERACT_ACROSS_USERS,
+                        p) == PackageManager.PERMISSION_GRANTED)
+                ));
+        boolean hasInteractAcrossProfilesAppOp = Arrays.stream(uidPackages).anyMatch(
+                p -> (AppOpsManager.MODE_ALLOWED == mAppOpsManager.checkOpNoThrow(
+                        op, serviceInfo.applicationInfo.uid, p))
+        );
+        Log.i(this,
+                "packageName:%s INTERACT_ACROSS_USERS:%b INTERACT_ACROSS_PROFILES:%b "
+                        + "INTERACT_ACROSS_PROFILES_APPOP:%b",
+                uidPackages[0], hasInteractAcrossUsers, hasInteractAcrossProfiles,
+                hasInteractAcrossProfilesAppOp);
+
+        return (hasInteractAcrossUsers || hasInteractAcrossProfiles
+                || hasInteractAcrossProfilesAppOp);
+    }
 
     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
             String packageName, ComponentName componentName,
@@ -1867,11 +2084,16 @@
         if (componentName != null) {
             serviceIntent.setComponent(componentName);
         }
+        Log.i(this,
+                "getComponents, pkgname: " + packageName + " comp: " + componentName + " userid: "
+                        + userHandle.getIdentifier() + " requestedType: " + requestedType);
         PackageManager packageManager = mContext.getPackageManager();
         Context userContext = mContext.createContextAsUser(userHandle,
                 0 /* flags */);
         PackageManager userPackageManager = userContext != null ?
                 userContext.getPackageManager() : packageManager;
+
+
         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
                 serviceIntent,
                 PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS,
@@ -1888,6 +2110,10 @@
 
                 int currentType = getInCallServiceType(userHandle,
                         entry.serviceInfo, packageManager, packageName);
+
+                boolean hasInteractAcrossUserOrProfilePerm = canInteractAcrossUsersOrProfiles(
+                        entry.serviceInfo, packageManager);
+
                 ComponentName foundComponentName =
                         new ComponentName(serviceInfo.packageName, serviceInfo.name);
                 if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
@@ -1903,9 +2129,16 @@
                     isRequestedType = requestedType == currentType;
                 }
 
+                Log.i(this,
+                        "found:%s isRequestedtype:%b isEnabled:%b ignoreDisabled:%b "
+                                + "hasCrossProfilePerm:%b",
+                        foundComponentName, isRequestedType, isEnabled, ignoreDisabled,
+                        hasInteractAcrossUserOrProfilePerm);
+
                 if ((!ignoreDisabled || isEnabled) && isRequestedType) {
                     retval.add(new InCallServiceInfo(foundComponentName, isExternalCallsSupported,
-                            isSelfManageCallsSupported, requestedType));
+                            isSelfManageCallsSupported, requestedType,
+                            hasInteractAcrossUserOrProfilePerm));
                 }
             }
         }
@@ -2439,19 +2672,48 @@
         Log.i(this, "updateCarModeForConnections: car mode apps: %s",
                 mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
 
-        if (mInCallServiceConnections.containsKey(mCallsManager.getCurrentUserHandle())) {
-            CarSwappingInCallServiceConnection inCallServiceConnection = mInCallServiceConnections.
-                    get(mCallsManager.getCurrentUserHandle());
-            if (shouldUseCarModeUI()) {
-                Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
-                inCallServiceConnection.changeCarModeApp(mCarModeTracker.getCurrentCarModePackage(),
-                        mCallsManager.getCurrentUserHandle());
-            } else {
-                if (inCallServiceConnection.isCarMode()) {
-                    Log.i(this, "updateCarModeForConnections: car mode no longer "
-                            + "applicable; disabling");
-                    inCallServiceConnection.disableCarMode();
-                }
+        UserManager um = mContext.getSystemService(UserManager.class);
+        UserHandle currentUser = mCallsManager.getCurrentUserHandle();
+        UserHandle childUser = findChildManagedProfileUser(currentUser, um);
+
+        CarSwappingInCallServiceConnection inCallServiceConnectionForCurrentUser = null;
+        CarSwappingInCallServiceConnection inCallServiceConnectionForChildUser = null;
+
+        Log.i(this, "update carmode current:%s parent:%s", currentUser, childUser);
+        if (mInCallServiceConnections.containsKey(currentUser)) {
+            inCallServiceConnectionForCurrentUser = mInCallServiceConnections.
+                    get(currentUser);
+        }
+        if (childUser != null && mInCallServiceConnections.containsKey(childUser)) {
+            inCallServiceConnectionForChildUser = mInCallServiceConnections.
+                    get(childUser);
+        }
+
+        if (shouldUseCarModeUI()) {
+            Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
+            //always pass current user to changeCarMode. That will ultimately be used for bindAsUser
+            if (inCallServiceConnectionForCurrentUser != null) {
+                inCallServiceConnectionForCurrentUser.changeCarModeApp(
+                        mCarModeTracker.getCurrentCarModePackage(),
+                        currentUser);
+            }
+            if (inCallServiceConnectionForChildUser != null) {
+                inCallServiceConnectionForChildUser.changeCarModeApp(
+                        mCarModeTracker.getCurrentCarModePackage(),
+                        currentUser);
+            }
+        } else {
+            if (inCallServiceConnectionForCurrentUser != null
+                    && inCallServiceConnectionForCurrentUser.isCarMode()) {
+                Log.i(this, "updateCarModeForConnections: car mode no longer "
+                        + "applicable for current user; disabling");
+                inCallServiceConnectionForCurrentUser.disableCarMode();
+            }
+            if (inCallServiceConnectionForChildUser != null
+                    && inCallServiceConnectionForChildUser.isCarMode()) {
+                Log.i(this, "updateCarModeForConnections: car mode no longer "
+                        + "applicable for child user; disabling");
+                inCallServiceConnectionForChildUser.disableCarMode();
             }
         }
     }
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 4c8d8e2..d87ce7b 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.ArgumentMatchers.nullable;
@@ -154,6 +155,7 @@
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
     @Mock UserManager mMockUserManager;
     @Mock UserInfo mMockUserInfo;
+    @Mock UserInfo mMockChildUserInfo; //work profile
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -198,6 +200,10 @@
         | Context.BIND_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
         | Context.BIND_SCHEDULE_LIKE_TOP_APP;
 
+    private UserHandle mChildUserHandle = UserHandle.of(10);
+    private @Mock Call mMockChildUserCall;
+    private UserHandle mParentUserHandle = UserHandle.of(1);
+
     @Override
     @Before
     public void setUp() throws Exception {
@@ -296,6 +302,8 @@
         when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
 
         when(mMockContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mMockUserManager);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+                .thenReturn(mMockUserManager);
         // Mock user info to allow binding on user stored in the phone account (mUserHandle).
         when(mMockUserManager.getUserInfo(anyInt())).thenReturn(mMockUserInfo);
         when(mMockUserInfo.isManagedProfile()).thenReturn(true);
@@ -1736,6 +1744,69 @@
                 eq(UserHandle.CURRENT));
     }
 
+    private void setupMocksForWorkProfileTest() {
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockChildUserCall.isIncoming()).thenReturn(false);
+        when(mMockChildUserCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+        when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
+        when(mMockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
+                anyInt(), any())).thenReturn(true);
+        when(mMockChildUserCall.isExternalCall()).thenReturn(false);
+        when(mMockChildUserCall.isSelfManaged()).thenReturn(true);
+        when(mMockChildUserCall.visibleToInCallService()).thenReturn(true);
+
+        //Setup up parent and child/work profile relation
+        when(mMockUserInfo.getUserHandle()).thenReturn(mParentUserHandle);
+        when(mMockChildUserInfo.getUserHandle()).thenReturn(mChildUserHandle);
+        when(mMockUserInfo.isManagedProfile()).thenReturn(false);
+        when(mMockChildUserInfo.isManagedProfile()).thenReturn(true);
+        when(mMockChildUserCall.getUserHandleFromTargetPhoneAccount()).thenReturn(mChildUserHandle);
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mChildUserHandle);
+        when(mMockUserManager.getProfileParent(mChildUserHandle.getIdentifier())).thenReturn(
+                mMockUserInfo);
+        when(mMockUserManager.getProfileParent(mChildUserHandle)).thenReturn(mParentUserHandle);
+        when(mMockUserManager.getUserInfo(eq(mParentUserHandle.getIdentifier()))).thenReturn(
+                mMockUserInfo);
+        when(mMockUserManager.getUserInfo(eq(mChildUserHandle.getIdentifier()))).thenReturn(
+                mMockChildUserInfo);
+        when(mMockUserManager.isManagedProfile(mChildUserHandle.getIdentifier())).thenReturn(true);
+        when(mMockUserManager.isManagedProfile(mParentUserHandle.getIdentifier())).thenReturn(
+                false);
+    }
+
+    @Test
+    public void testManagedProfileCallQueriesIcsUsingParentUserToo() throws Exception {
+        setupMocksForWorkProfileTest();
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManager(true /* default */,
+                true /*useNonUiInCalls*/, true /*useAppOpNonUiInCalls*/,
+                true /*useSystemDialer*/, false /*includeExternalCalls*/,
+                true /*includeSelfManagedCallsInDefaultDialer*/,
+                true /*includeSelfManagedCallsInCarModeDialer*/,
+                true /*includeSelfManagedCallsInNonUi*/);
+
+        //pass in call by child/work-profileuser
+        mInCallController.bindToServices(mMockChildUserCall);
+
+        // Verify that queryIntentServicesAsUser is also called with parent handle
+        // Query for the different InCallServices
+        ArgumentCaptor<Integer> userIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<Integer> flagCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMockPackageManager, times(6)).queryIntentServicesAsUser(
+                queryIntentCaptor.capture(), flagCaptor.capture(), userIdCaptor.capture());
+        List<Integer> userIds = userIdCaptor.getAllValues();
+
+        //check if queryIntentServices was called with child user handle
+        assertTrue("no query parent user handle",
+                userIds.contains(mChildUserHandle.getIdentifier()));
+        //check if queryIntentServices was also called with parent user handle
+        assertTrue("no query parent user handle",
+                userIds.contains(mParentUserHandle.getIdentifier()));
+
+    }
+
     private void setupMocks(boolean isExternalCall) {
         setupMocks(isExternalCall, false /* isSelfManagedCall */);
     }