Add *AsUser() calls where appropriate for multi-user support.
Although telecom always runs as system user, it binds to apps in the
user space and so needs to interact with apps with the current user
explicitly. This change fixes areas that neglected to do that.
Bug: 27161171
Change-Id: I55a4f612c9b38d4e448c346173845a79a926c695
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 7b342bc..62d002f 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1319,7 +1319,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 34f1a49..f0a8998 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;
@@ -108,7 +109,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.
@@ -141,6 +143,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;
@@ -229,8 +236,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)
@@ -242,7 +250,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);
@@ -538,6 +545,10 @@
return mCallAudioManager.getForegroundCall();
}
+ public UserHandle getCurrentUserHandle() {
+ return mCurrentUserHandle;
+ }
+
CallAudioManager getCallAudioManager() {
return mCallAudioManager;
}
@@ -546,7 +557,8 @@
return mInCallController;
}
- boolean hasEmergencyCall() {
+ @VisibleForTesting
+ public boolean hasEmergencyCall() {
for (Call call : mCalls) {
if (call.isEmergencyCall()) {
return true;
@@ -1841,6 +1853,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 a3766ec..db85ee4 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;
}
@@ -139,7 +142,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));
+ }
+}