Merge "Add *AsUser() calls where appropriate for multi-user support." into nyc-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index fc124a4..2a56422 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1331,7 +1331,8 @@
         }
     }
 
-    Bundle getIntentExtras() {
+    @VisibleForTesting
+    public Bundle getIntentExtras() {
         return mIntentExtras;
     }
 
diff --git a/src/com/android/server/telecom/CallScreening.java b/src/com/android/server/telecom/CallScreening.java
index f0ef5a6..22201a3 100644
--- a/src/com/android/server/telecom/CallScreening.java
+++ b/src/com/android/server/telecom/CallScreening.java
@@ -55,6 +55,7 @@
     private final Listener mListener;
     private final TelecomSystem.SyncRoot mLock;
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
+    private final CallsManager mCallsManager;
     private final Handler mHandler = new Handler();
     private Call mCall;
     private ICallScreeningService mService;
@@ -62,12 +63,13 @@
 
     public CallScreening(
             Context context,
-            Listener listener,
+            CallsManager callsManager,
             TelecomSystem.SyncRoot lock,
             PhoneAccountRegistrar phoneAccountRegistrar,
             Call call) {
         mContext = context;
-        mListener = listener;
+        mCallsManager = callsManager;
+        mListener = callsManager;
         mLock = lock;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mCall = call;
@@ -113,7 +115,8 @@
 
         Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
             .setPackage(dialerPackage);
-        List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServices(intent, 0);
+        List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
+                intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
         if (entries.isEmpty()) {
             return false;
         }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 8379ddf..2634ab3 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.content.Intent;
@@ -109,7 +110,8 @@
             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING};
 
     private static final int[] LIVE_CALL_STATES =
-            {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.ACTIVE};
+            {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
+                CallState.ACTIVE};
     public static final String TELECOM_CALL_ID_PREFIX = "TC@";
 
     // Maps call technologies in PhoneConstants to those in Analytics.
@@ -142,6 +144,11 @@
      */
     private int mCallId = 0;
 
+    /**
+     * Stores the current foreground user.
+     */
+    private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
+
     private final ConnectionServiceRepository mConnectionServiceRepository;
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
     private final InCallController mInCallController;
@@ -230,8 +237,9 @@
         RingtoneFactory ringtoneFactory = new RingtoneFactory(context);
         SystemVibrator systemVibrator = new SystemVibrator(context);
         AsyncRingtonePlayer asyncRingtonePlayer = new AsyncRingtonePlayer();
+        mInCallController = new InCallController(context, mLock, this, systemStateProvider);
         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
-                ringtoneFactory, systemVibrator);
+                ringtoneFactory, systemVibrator, mInCallController);
 
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                 this,new CallAudioModeStateMachine((AudioManager)
@@ -243,7 +251,6 @@
         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
         mCallLogManager = new CallLogManager(context, phoneAccountRegistrar);
-        mInCallController = new InCallController(context, mLock, this, systemStateProvider);
         mConnectionServiceRepository =
                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
@@ -553,6 +560,10 @@
         return mCallAudioManager.getForegroundCall();
     }
 
+    public UserHandle getCurrentUserHandle() {
+        return mCurrentUserHandle;
+    }
+
     CallAudioManager getCallAudioManager() {
         return mCallAudioManager;
     }
@@ -561,7 +572,8 @@
         return mInCallController;
     }
 
-    boolean hasEmergencyCall() {
+    @VisibleForTesting
+    public boolean hasEmergencyCall() {
         for (Call call : mCalls) {
             if (call.isEmergencyCall()) {
                 return true;
@@ -1848,6 +1860,7 @@
      * including the user itself. There may be chances that profiles are not started yet.
      */
     void onUserSwitch(UserHandle userHandle) {
+        mCurrentUserHandle = userHandle;
         mMissedCallNotifier.setCurrentUserHandle(userHandle);
         final UserManager userManager = UserManager.get(mContext);
         List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier());
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 41c99db..150879d 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -39,6 +39,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.VisibleForTesting;
 // TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.telecom.IInCallService;
 import com.android.internal.util.IndentingPrintWriter;
@@ -345,7 +346,8 @@
      *
      * @param call The newly added call that triggered the binding to the in-call services.
      */
-    private void bindToServices(Call call) {
+    @VisibleForTesting
+    public void bindToServices(Call call) {
         ComponentName inCallUIService = null;
         ComponentName carModeInCallUIService = null;
         List<ComponentName> nonUIInCallServices = new LinkedList<>();
@@ -353,12 +355,16 @@
         // Loop through all the InCallService implementations that exist in the devices;
         PackageManager packageManager = mContext.getPackageManager();
         Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
-        for (ResolveInfo entry :
-                packageManager.queryIntentServices(serviceIntent, PackageManager.GET_META_DATA)) {
+        for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
+                serviceIntent,
+                PackageManager.GET_META_DATA,
+                mCallsManager.getCurrentUserHandle().getIdentifier())) {
             ServiceInfo serviceInfo = entry.serviceInfo;
+
             if (serviceInfo != null) {
                 ComponentName componentName =
                         new ComponentName(serviceInfo.packageName, serviceInfo.name);
+                Log.v(this, "ICS: " + componentName + ", user: " + entry.targetUserId);
 
                 switch (getInCallServiceType(entry.serviceInfo, packageManager)) {
                     case IN_CALL_SERVICE_TYPE_DIALER_UI:
@@ -500,7 +506,8 @@
 
         // Check to see that it is the default dialer package
         boolean isDefaultDialerPackage = Objects.equals(serviceInfo.packageName,
-                DefaultDialerManager.getDefaultDialerApplication(mContext));
+                DefaultDialerManager.getDefaultDialerApplication(
+                    mContext, mCallsManager.getCurrentUserHandle().getIdentifier()));
         boolean isUIService = serviceInfo.metaData != null &&
                 serviceInfo.metaData.getBoolean(
                         TelecomManager.METADATA_IN_CALL_SERVICE_UI, false);
@@ -706,17 +713,26 @@
         pw.decreaseIndent();
     }
 
-    static boolean doesDefaultDialerSupportRinging(Context context) {
-        String dialerPackage = DefaultDialerManager
-                .getDefaultDialerApplication(context, UserHandle.USER_CURRENT);
-        if (TextUtils.isEmpty(dialerPackage)) {
+    public boolean doesConnectedDialerSupportRinging() {
+        String ringingPackage =  null;
+        if (mInCallUIComponentName != null) {
+            ringingPackage = mInCallUIComponentName.getPackageName().trim();
+        }
+
+        if (TextUtils.isEmpty(ringingPackage)) {
+            // The current in-call UI returned nothing, so lets use the default dialer.
+            ringingPackage = DefaultDialerManager.getDefaultDialerApplication(
+                    mContext, UserHandle.USER_CURRENT);
+        }
+        if (TextUtils.isEmpty(ringingPackage)) {
             return false;
         }
 
         Intent intent = new Intent(InCallService.SERVICE_INTERFACE)
-            .setPackage(dialerPackage);
-        List<ResolveInfo> entries = context.getPackageManager()
-                .queryIntentServices(intent, PackageManager.GET_META_DATA);
+            .setPackage(ringingPackage);
+        List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
+                intent, PackageManager.GET_META_DATA,
+                mCallsManager.getCurrentUserHandle().getIdentifier());
         if (entries.isEmpty()) {
             return false;
         }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index b9d6e0d..ddd2e93 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -56,6 +56,7 @@
     private final AsyncRingtonePlayer mRingtonePlayer;
     private final Context mContext;
     private final Vibrator mVibrator;
+    private final InCallController mInCallController;
 
     private InCallTonePlayer mCallWaitingPlayer;
     private RingtoneFactory mRingtoneFactory;
@@ -79,7 +80,8 @@
             SystemSettingsUtil systemSettingsUtil,
             AsyncRingtonePlayer asyncRingtonePlayer,
             RingtoneFactory ringtoneFactory,
-            Vibrator vibrator) {
+            Vibrator vibrator,
+            InCallController inCallController) {
 
         mSystemSettingsUtil = systemSettingsUtil;
         mPlayerFactory = playerFactory;
@@ -89,6 +91,7 @@
         mVibrator = vibrator;
         mRingtonePlayer = asyncRingtonePlayer;
         mRingtoneFactory = ringtoneFactory;
+        mInCallController = inCallController;
     }
 
     public void startRinging(Call foregroundCall) {
@@ -101,7 +104,7 @@
             return;
         }
 
-        if (InCallController.doesDefaultDialerSupportRinging(mContext)) {
+        if (mInCallController.doesConnectedDialerSupportRinging()) {
             Log.event(foregroundCall, Log.Events.SKIP_RINGING);
             return;
         }
@@ -138,7 +141,7 @@
             return;
         }
 
-        if (InCallController.doesDefaultDialerSupportRinging(mContext)) {
+        if (mInCallController.doesConnectedDialerSupportRinging()) {
             Log.event(call, Log.Events.SKIP_RINGING);
             return;
         }
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index e645123..3e8fe5b 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -54,7 +54,7 @@
         <service android:name="com.android.server.telecom.testapps.TestInCallServiceImpl"
                  android:process="com.android.server.telecom.testapps.TestInCallService"
                  android:permission="android.permission.BIND_INCALL_SERVICE" >
-            <meta-data android:name="android.telecom.IN_CALL_SERVICE_CAR_MODE_UI" android:value="true" />
+            <meta-data android:name="android.telecom.IN_CALL_SERVICE_CAR_MODE_UI" android:value="true"/>
             <intent-filter>
                 <action android:name="android.telecom.InCallService"/>
             </intent-filter>
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
new file mode 100644
index 0000000..ee3f691
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.telecom.ConnectionService;
+import android.telecom.InCallService;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.test.mock.MockContext;
+
+import com.android.server.telecom.BluetoothHeadsetProxy;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.InCallController;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.R;
+import com.android.server.telecom.SystemStateProvider;
+import com.android.server.telecom.TelecomSystem;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.LinkedList;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyChar;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+public class InCallControllerTests extends TelecomTestCase {
+    @Mock CallsManager mMockCallsManager;
+    @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
+    @Mock BluetoothHeadsetProxy mMockBluetoothHeadset;
+    @Mock SystemStateProvider mMockSystemStateProvider;
+    @Mock PackageManager mMockPackageManager;
+    @Mock Call mMockCall;
+    @Mock Resources mMockResources;
+    @Mock MockContext mMockContext;
+
+    private static final int CURRENT_USER_ID = 900973;
+    private static final String DEF_PKG = "defpkg";
+    private static final String DEF_CLASS = "defcls";
+    private static final PhoneAccountHandle PA_HANDLE =
+            new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id");
+
+    private UserHandle mUserHandle = UserHandle.of(CURRENT_USER_ID);
+    private InCallController mInCallController;
+    private TelecomSystem.SyncRoot mLock;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        doReturn(mMockResources).when(mMockContext).getResources();
+        doReturn(DEF_PKG).when(mMockResources).getString(R.string.ui_default_package);
+        doReturn(DEF_CLASS).when(mMockResources).getString(R.string.incall_default_class);
+        mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
+                mMockSystemStateProvider);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testBindToService_NoServicesFound_IncomingCall() throws Exception {
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCallsManager.hasEmergencyCall()).thenReturn(false);
+        when(mMockCall.isIncoming()).thenReturn(true);
+
+        Intent queryIntent = new Intent(InCallService.SERVICE_INTERFACE);
+        when(mMockPackageManager.queryIntentServicesAsUser(
+                queryIntent, PackageManager.GET_META_DATA, CURRENT_USER_ID))
+            .thenReturn(new LinkedList<ResolveInfo>());
+        mInCallController.bindToServices(mMockCall);
+
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.CURRENT));
+
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+        assertEquals(DEF_PKG, bindIntent.getComponent().getPackageName());
+        assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName());
+        assertNull(bindIntent.getExtras());
+    }
+
+    public void testBindToService_NoServicesFound_OutgoingCall() throws Exception {
+        Bundle callExtras = new Bundle();
+        callExtras.putBoolean("whatever", true);
+
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCallsManager.hasEmergencyCall()).thenReturn(false);
+        when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+        when(mMockCall.getIntentExtras()).thenReturn(callExtras);
+
+        Intent queryIntent = new Intent(InCallService.SERVICE_INTERFACE);
+        when(mMockPackageManager.queryIntentServicesAsUser(
+                queryIntent, PackageManager.GET_META_DATA, CURRENT_USER_ID))
+            .thenReturn(new LinkedList<ResolveInfo>());
+        mInCallController.bindToServices(mMockCall);
+
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.CURRENT));
+
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+        assertEquals(DEF_PKG, bindIntent.getComponent().getPackageName());
+        assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName());
+        assertEquals(PA_HANDLE, bindIntent.getExtras().getParcelable(
+                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
+        assertEquals(callExtras, bindIntent.getExtras().getParcelable(
+                TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS));
+    }
+}