Merge "Change hasVoicemailNumber to getVoicemailNumber"
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 4b1cef5..7edda41 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -42,7 +42,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.IVideoProvider;
import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.CallerInfoAsyncQuery;
import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
import com.android.internal.telephony.SmsApplication;
import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener;
@@ -152,25 +151,29 @@
public void onCallSubstateChanged(Call call) {}
}
- private static final OnQueryCompleteListener sCallerInfoQueryListener =
+ private final OnQueryCompleteListener mCallerInfoQueryListener =
new OnQueryCompleteListener() {
/** ${inheritDoc} */
@Override
public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
- if (cookie != null) {
- ((Call) cookie).setCallerInfo(callerInfo, token);
+ synchronized (mLock) {
+ if (cookie != null) {
+ ((Call) cookie).setCallerInfo(callerInfo, token);
+ }
}
}
};
- private static final OnImageLoadCompleteListener sPhotoLoadListener =
+ private final OnImageLoadCompleteListener mPhotoLoadListener =
new OnImageLoadCompleteListener() {
/** ${inheritDoc} */
@Override
public void onImageLoadComplete(
int token, Drawable photo, Bitmap photoIcon, Object cookie) {
- if (cookie != null) {
- ((Call) cookie).setPhoto(photo, photoIcon, token);
+ synchronized (mLock) {
+ if (cookie != null) {
+ ((Call) cookie).setPhoto(photo, photoIcon, token);
+ }
}
}
};
@@ -310,6 +313,7 @@
private final CallsManager mCallsManager;
private final TelecomSystem.SyncRoot mLock;
private int mCallSubstate;
+ private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
private boolean mWasConferencePreviouslyMerged = false;
@@ -341,6 +345,7 @@
TelecomSystem.SyncRoot lock,
ConnectionServiceRepository repository,
ContactsAsyncHelper contactsAsyncHelper,
+ CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Uri handle,
GatewayInfo gatewayInfo,
PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -353,6 +358,7 @@
mLock = lock;
mRepository = repository;
mContactsAsyncHelper = contactsAsyncHelper;
+ mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
setHandle(handle);
setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
mGatewayInfo = gatewayInfo;
@@ -384,6 +390,7 @@
TelecomSystem.SyncRoot lock,
ConnectionServiceRepository repository,
ContactsAsyncHelper contactsAsyncHelper,
+ CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Uri handle,
GatewayInfo gatewayInfo,
PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -391,7 +398,8 @@
boolean isIncoming,
boolean isConference,
long connectTimeMillis) {
- this(context, callsManager, lock, repository, contactsAsyncHelper, handle, gatewayInfo,
+ this(context, callsManager, lock, repository, contactsAsyncHelper,
+ callerInfoAsyncQueryFactory, handle, gatewayInfo,
connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, isIncoming,
isConference);
@@ -416,18 +424,48 @@
component = mConnectionService.getComponentName().flattenToShortString();
}
- return String.format(Locale.US, "[%s, %s, %s, %s, %d, childs(%d), has_parent(%b), [%s], %d]",
+
+
+ return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), [%s], %d]",
System.identityHashCode(this),
CallState.toString(mState),
component,
Log.piiHandle(mHandle),
- getVideoState(),
+ getVideoStateDescription(getVideoState()),
getChildCalls().size(),
getParentCall() != null,
Connection.capabilitiesToString(getConnectionCapabilities()),
getCallSubstate());
}
+ /**
+ * Builds a debug-friendly description string for a video state.
+ * <p>
+ * A = audio active, T = video transmission active, R = video reception active, P = video
+ * paused.
+ *
+ * @param videoState The video state.
+ * @return A string indicating which bits are set in the video state.
+ */
+ private String getVideoStateDescription(int videoState) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("A");
+
+ if (VideoProfile.VideoState.isTransmissionEnabled(videoState)) {
+ sb.append("T");
+ }
+
+ if (VideoProfile.VideoState.isReceptionEnabled(videoState)) {
+ sb.append("R");
+ }
+
+ if (VideoProfile.VideoState.isPaused(videoState)) {
+ sb.append("P");
+ }
+
+ return sb.toString();
+ }
+
int getState() {
return mState;
}
@@ -1269,11 +1307,11 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- CallerInfoAsyncQuery.startQuery(
+ mCallerInfoAsyncQueryFactory.startQuery(
mQueryToken,
mContext,
number,
- sCallerInfoQueryListener,
+ mCallerInfoQueryListener,
Call.this);
}
});
@@ -1302,7 +1340,7 @@
token,
mContext,
mCallerInfo.contactDisplayPhotoUri,
- sPhotoLoadListener,
+ mPhotoLoadListener,
this);
// Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
} else {
diff --git a/src/com/android/server/telecom/CallerInfoAsyncQueryFactory.java b/src/com/android/server/telecom/CallerInfoAsyncQueryFactory.java
new file mode 100644
index 0000000..149470f
--- /dev/null
+++ b/src/com/android/server/telecom/CallerInfoAsyncQueryFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.internal.telephony.CallerInfoAsyncQuery;
+
+import android.content.Context;
+
+public interface CallerInfoAsyncQueryFactory {
+ CallerInfoAsyncQuery startQuery(int token, Context context, String number,
+ CallerInfoAsyncQuery.OnQueryCompleteListener listener, Object cookie);
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 371660a..5fd4fca 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -129,6 +129,7 @@
private final Context mContext;
private final TelecomSystem.SyncRoot mLock;
private final ContactsAsyncHelper mContactsAsyncHelper;
+ private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final MissedCallNotifier mMissedCallNotifier;
private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
@@ -153,6 +154,7 @@
Context context,
TelecomSystem.SyncRoot lock,
ContactsAsyncHelper contactsAsyncHelper,
+ CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
MissedCallNotifier missedCallNotifier,
PhoneAccountRegistrar phoneAccountRegistrar,
HeadsetMediaButtonFactory headsetMediaButtonFactory,
@@ -161,6 +163,7 @@
mContext = context;
mLock = lock;
mContactsAsyncHelper = contactsAsyncHelper;
+ mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
mPhoneAccountRegistrar = phoneAccountRegistrar;
mMissedCallNotifier = missedCallNotifier;
StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
@@ -168,7 +171,7 @@
mDockManager = new DockManager(context);
mCallAudioManager = new CallAudioManager(
context, statusBarNotifier, mWiredHeadsetManager, mDockManager, this);
- InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager);
+ InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager, lock);
mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this);
mTtyManager = new TtyManager(context, mWiredHeadsetManager);
@@ -194,7 +197,8 @@
mListeners.add(mHeadsetMediaButton);
mListeners.add(mProximitySensorManager);
- mMissedCallNotifier.updateOnStartup(mLock, this, mContactsAsyncHelper);
+ mMissedCallNotifier.updateOnStartup(
+ mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory);
}
public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
@@ -446,6 +450,7 @@
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -468,6 +473,7 @@
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -507,6 +513,7 @@
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -1111,6 +1118,7 @@
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
null /* handle */,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -1462,6 +1470,7 @@
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
connection.getHandle() /* handle */,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
diff --git a/src/com/android/server/telecom/ContactsAsyncHelper.java b/src/com/android/server/telecom/ContactsAsyncHelper.java
index ef7ea4c..44fa654 100644
--- a/src/com/android/server/telecom/ContactsAsyncHelper.java
+++ b/src/com/android/server/telecom/ContactsAsyncHelper.java
@@ -127,12 +127,12 @@
}
}
}
- synchronized (mLock) {
- Log.d(this, "Notifying listener: " + args.listener.toString() +
- " image: " + args.displayPhotoUri + " completed");
- args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon,
+
+ // Listener will synchronize as needed
+ Log.d(this, "Notifying listener: " + args.listener.toString() +
+ " image: " + args.displayPhotoUri + " completed");
+ args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon,
args.cookie);
- }
break;
default:
break;
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 4e958ad..4c44bd7 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -624,7 +624,10 @@
android.telecom.Call.Details.CAPABILITY_SHOW_CALLBACK_NUMBER,
Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
- android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO
+ android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
+
+ Connection.CAPABILITY_CAN_PAUSE_VIDEO,
+ android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO
};
private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 3b4380e..f51a639 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -33,13 +33,15 @@
*/
public static class Factory {
private final CallAudioManager mCallAudioManager;
+ private final TelecomSystem.SyncRoot mLock;
- Factory(CallAudioManager callAudioManager) {
+ Factory(CallAudioManager callAudioManager, TelecomSystem.SyncRoot lock) {
mCallAudioManager = callAudioManager;
+ mLock = lock;
}
InCallTonePlayer createPlayer(int tone) {
- return new InCallTonePlayer(tone, mCallAudioManager);
+ return new InCallTonePlayer(tone, mCallAudioManager, mLock);
}
}
@@ -89,15 +91,22 @@
/** Current state of the tone player. */
private int mState;
+ /** Telecom lock object. */
+ private final TelecomSystem.SyncRoot mLock;
+
/**
* Initializes the tone player. Private; use the {@link Factory} to create tone players.
*
* @param toneId ID of the tone to play, see TONE_* constants.
*/
- private InCallTonePlayer(int toneId, CallAudioManager callAudioManager) {
+ private InCallTonePlayer(
+ int toneId,
+ CallAudioManager callAudioManager,
+ TelecomSystem.SyncRoot lock) {
mState = STATE_OFF;
mToneId = toneId;
mCallAudioManager = callAudioManager;
+ mLock = lock;
}
/** {@inheritDoc} */
@@ -247,10 +256,12 @@
// Release focus on the main thread.
mMainThreadHandler.post(new Runnable() {
@Override public void run() {
- if (sTonesPlaying == 0) {
- Log.wtf(this, "Over-releasing focus for tone player.");
- } else if (--sTonesPlaying == 0) {
- mCallAudioManager.setIsTonePlaying(false);
+ synchronized (mLock) {
+ if (sTonesPlaying == 0) {
+ Log.wtf(this, "Over-releasing focus for tone player.");
+ } else if (--sTonesPlaying == 0) {
+ mCallAudioManager.setIsTonePlaying(false);
+ }
}
}
});
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 52055cf..5a88c34 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -28,5 +28,6 @@
void updateOnStartup(
TelecomSystem.SyncRoot lock,
CallsManager callsManager,
- ContactsAsyncHelper contactsAsyncHelper);
+ ContactsAsyncHelper contactsAsyncHelper,
+ CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory);
}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index f780b6f..a264702 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -409,7 +409,7 @@
@Override
public void silenceRinger() {
synchronized (mLock) {
- enforceModifyPermission();
+ enforceModifyPermissionOrDefaultDialer();
mCallsManager.getRinger().silence();
}
}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 22377c9..c3ab0dc 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -92,6 +92,7 @@
public TelecomSystem(
Context context,
MissedCallNotifier missedCallNotifier,
+ CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
HeadsetMediaButtonFactory headsetMediaButtonFactory,
ProximitySensorManagerFactory proximitySensorManagerFactory,
InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
@@ -105,6 +106,7 @@
mContext,
mLock,
mContactsAsyncHelper,
+ callerInfoAsyncQueryFactory,
mMissedCallNotifier,
mPhoneAccountRegistrar,
headsetMediaButtonFactory,
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 49b4aa4..f7f4054 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -22,6 +22,8 @@
import android.content.Intent;
import android.os.IBinder;
+import com.android.internal.telephony.CallerInfoAsyncQuery;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.HeadsetMediaButton;
import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -63,6 +65,17 @@
new TelecomSystem(
context,
new MissedCallNotifierImpl(context.getApplicationContext()),
+ new CallerInfoAsyncQueryFactory() {
+ @Override
+ public CallerInfoAsyncQuery startQuery(int token, Context context,
+ String number,
+ CallerInfoAsyncQuery.OnQueryCompleteListener listener,
+ Object cookie) {
+ Log.i(TelecomSystem.getInstance(), "CallerInfoAsyncQuery.startQuery number=%s cookie=%s", number, cookie);
+ return CallerInfoAsyncQuery.startQuery(
+ token, context, number, listener, cookie);
+ }
+ },
new HeadsetMediaButtonFactory() {
@Override
public HeadsetMediaButton create(Context context,
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index d7e9759..11fca20 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -17,6 +17,7 @@
package com.android.server.telecom.ui;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.CallsManagerListenerBase;
import com.android.server.telecom.Constants;
@@ -312,7 +313,8 @@
public void updateOnStartup(
final TelecomSystem.SyncRoot lock,
final CallsManager callsManager,
- final ContactsAsyncHelper contactsAsyncHelper) {
+ final ContactsAsyncHelper contactsAsyncHelper,
+ final CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory) {
Log.d(this, "updateOnStartup()...");
// instantiate query handler
@@ -343,8 +345,8 @@
// Convert the data to a call object
Call call = new Call(mContext, callsManager, lock,
- null, contactsAsyncHelper, null, null, null, null, true,
- false);
+ null, contactsAsyncHelper, callerInfoAsyncQueryFactory,
+ null, null, null, null, true, false);
call.setDisconnectCause(
new DisconnectCause(DisconnectCause.MISSED));
call.setState(CallState.DISCONNECTED);
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 747d377..1b296d4 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -66,6 +66,15 @@
<data android:scheme="tel" />
<data android:scheme="sip" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.telecom.testapps.ACTION_HANGUP_CALLS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.telecom.testapps.ACTION_SEND_UPGRADE_REQUEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="int" />
+ </intent-filter>
</activity>
<receiver android:name="com.android.server.telecom.testapps.CallNotificationReceiver"
diff --git a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
index 0589a8e..36a3493 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
@@ -112,4 +112,11 @@
LocalBroadcastManager.getInstance(context).sendBroadcast(
new Intent(TestCallActivity.ACTION_HANGUP_CALLS));
}
+
+ public static void sendUpgradeRequest(Context context, Uri data) {
+ Log.i(TAG, "Sending upgrade request of type: " + data);
+ final Intent intent = new Intent(TestCallActivity.ACTION_SEND_UPGRADE_REQUEST);
+ intent.setData(data);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+ }
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
index 38d2565..4ac151f 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -48,6 +48,9 @@
public static final String ACTION_HANGUP_CALLS =
"android.telecom.testapps.ACTION_HANGUP_CALLS";
+ public static final String ACTION_SEND_UPGRADE_REQUEST =
+ "android.telecom.testapps.ACTION_SEND_UPGRADE_REQUEST";
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -60,6 +63,8 @@
CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
} else if (ACTION_HANGUP_CALLS.equals(action)) {
CallNotificationReceiver.hangupCalls(this);
+ } else if (ACTION_SEND_UPGRADE_REQUEST.equals(action)) {
+ CallNotificationReceiver.sendUpgradeRequest(this, data);
} else {
CallServiceNotifier.getInstance().updateNotification(this);
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index 9f5d579..a5d833a 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -129,7 +129,7 @@
}
}
- private final class TestConnection extends Connection {
+ final class TestConnection extends Connection {
private final boolean mIsIncoming;
/** Used to cleanup camera and media when done with connection. */
@@ -144,6 +144,15 @@
}
};
+ private BroadcastReceiver mUpgradeRequestReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int request = Integer.parseInt(intent.getData().getSchemeSpecificPart());
+ final VideoProfile videoProfile = new VideoProfile(request);
+ mTestVideoCallProvider.receiveSessionModifyRequest(videoProfile);
+ }
+ };
+
TestConnection(boolean isIncoming) {
mIsIncoming = isIncoming;
// Assume all calls are video capable.
@@ -156,6 +165,11 @@
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
mHangupReceiver, new IntentFilter(TestCallActivity.ACTION_HANGUP_CALLS));
+ final IntentFilter filter =
+ new IntentFilter(TestCallActivity.ACTION_SEND_UPGRADE_REQUEST);
+ filter.addDataScheme("int");
+ LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
+ mUpgradeRequestReceiver, filter);
}
void startOutgoing() {
@@ -235,6 +249,8 @@
public void cleanup() {
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(
mHangupReceiver);
+ LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(
+ mUpgradeRequestReceiver);
}
/**
@@ -303,7 +319,7 @@
originalRequest.getExtras(),
originalRequest.getVideoState());
connection.setVideoState(originalRequest.getVideoState());
- maybeAddVideoProvider(connection);
+ addVideoProvider(connection);
addCall(connection);
connection.startOutgoing();
@@ -341,7 +357,7 @@
connection.setVideoState(videoState);
connection.setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
- maybeAddVideoProvider(connection);
+ addVideoProvider(connection);
addCall(connection);
@@ -383,15 +399,13 @@
}
}
- private void maybeAddVideoProvider(TestConnection connection) {
- if (connection.getVideoState() == VideoProfile.VideoState.BIDIRECTIONAL) {
- TestVideoProvider testVideoCallProvider =
- new TestVideoProvider(getApplicationContext());
- connection.setVideoProvider(testVideoCallProvider);
+ private void addVideoProvider(TestConnection connection) {
+ TestVideoProvider testVideoCallProvider =
+ new TestVideoProvider(getApplicationContext(), connection);
+ connection.setVideoProvider(testVideoCallProvider);
- // Keep reference to original so we can clean up the media players later.
- connection.setTestVideoCallProvider(testVideoCallProvider);
- }
+ // Keep reference to original so we can clean up the media players later.
+ connection.setTestVideoCallProvider(testVideoCallProvider);
}
private void activateCall(TestConnection connection) {
@@ -414,7 +428,7 @@
if (mCalls.isEmpty() && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
mMediaPlayer.release();
- mMediaPlayer = null;
+ mMediaPlayer = createMediaPlayer();
}
updateConferenceable();
diff --git a/testapps/src/com/android/server/telecom/testapps/TestVideoProvider.java b/testapps/src/com/android/server/telecom/testapps/TestVideoProvider.java
index 2dad471..f465243 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestVideoProvider.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestVideoProvider.java
@@ -20,6 +20,7 @@
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.server.telecom.testapps.R;
+import com.android.server.telecom.testapps.TestConnectionService.TestConnection;
import android.content.Context;
import android.graphics.SurfaceTexture;
@@ -52,6 +53,7 @@
* Implements the VideoCallProvider.
*/
public class TestVideoProvider extends Connection.VideoProvider {
+ private TestConnection mConnection;
private CameraCapabilities mCameraCapabilities;
private Random random;
private Surface mDisplaySurface;
@@ -65,11 +67,14 @@
private CameraCaptureSession mCameraSession;
private CameraThread mLooperThread;
+ private final Handler mHandler = new Handler();
+
private String mCameraId;
private static final long SESSION_TIMEOUT_MS = 2000;
- public TestVideoProvider(Context context) {
+ public TestVideoProvider(Context context, TestConnection connection) {
+ mConnection = connection;
mContext = context;
random = new Random();
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
@@ -140,15 +145,22 @@
* the response back via the CallVideoClient.
*/
@Override
- public void onSendSessionModifyRequest(VideoProfile requestProfile) {
+ public void onSendSessionModifyRequest(final VideoProfile requestProfile) {
log("Sent session modify request");
- VideoProfile responseProfile = new VideoProfile(
- requestProfile.getVideoState(), requestProfile.getQuality());
- receiveSessionModifyResponse(
- SESSION_MODIFY_REQUEST_SUCCESS,
- requestProfile,
- responseProfile);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ final VideoProfile responseProfile = new VideoProfile(
+ requestProfile.getVideoState(), requestProfile.getQuality());
+ mConnection.setVideoState(requestProfile.getVideoState());
+
+ receiveSessionModifyResponse(
+ SESSION_MODIFY_REQUEST_SUCCESS,
+ requestProfile,
+ responseProfile);
+ }
+ }, 2000);
}
@Override
diff --git a/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java b/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java
new file mode 100644
index 0000000..1ebf170
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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 com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.CallerInfoAsyncQuery;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
+import com.android.server.telecom.Log;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls a test {@link CallerInfoAsyncQueryFactory} to abstract away the asynchronous retrieval
+ * of caller information from the Android contacts database.
+ */
+public class CallerInfoAsyncQueryFactoryFixture implements
+ TestFixture<CallerInfoAsyncQueryFactory> {
+
+ static class Request {
+ int mToken;
+ Object mCookie;
+ CallerInfoAsyncQuery.OnQueryCompleteListener mListener;
+ void reply() {
+ mListener.onQueryComplete(mToken, mCookie, new CallerInfo());
+ }
+ }
+
+ CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory = new CallerInfoAsyncQueryFactory() {
+ @Override
+ public CallerInfoAsyncQuery startQuery(int token, Context context, String number,
+ CallerInfoAsyncQuery.OnQueryCompleteListener listener, Object cookie) {
+ Request r = new Request();
+ r.mToken = token;
+ r.mCookie = cookie;
+ r.mListener = listener;
+ mRequests.add(r);
+ return null;
+ }
+ };
+
+ final List<Request> mRequests = new ArrayList<>();
+
+ public CallerInfoAsyncQueryFactoryFixture() throws Exception {
+ Log.i(this, "Creating ...");
+ }
+
+ @Override
+ public CallerInfoAsyncQueryFactory getTestDouble() {
+ return mCallerInfoAsyncQueryFactory;
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index bf28da7..c83c7d4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -17,11 +17,15 @@
package com.android.server.telecom.tests;
import com.android.internal.telecom.IInCallAdapter;
+import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.CallerInfoAsyncQuery;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.HeadsetMediaButton;
import com.android.server.telecom.HeadsetMediaButtonFactory;
import com.android.server.telecom.InCallWakeLockController;
import com.android.server.telecom.InCallWakeLockControllerFactory;
+import com.android.server.telecom.Log;
import com.android.server.telecom.MissedCallNotifier;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
@@ -30,7 +34,6 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -48,6 +51,10 @@
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
@@ -125,6 +132,8 @@
ConnectionServiceFixture mConnectionServiceFixtureA;
ConnectionServiceFixture mConnectionServiceFixtureB;
+ CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
+
TelecomSystem mTelecomSystem;
@Override
@@ -157,6 +166,8 @@
InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
mock(InCallWakeLockControllerFactory.class);
+ mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
+
when(headsetMediaButtonFactory.create(
any(Context.class),
any(CallsManager.class)))
@@ -173,6 +184,7 @@
mTelecomSystem = new TelecomSystem(
mComponentContextFixture.getTestDouble(),
mMissedCallNotifier,
+ mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
headsetMediaButtonFactory,
proximitySensorManagerFactory,
inCallWakeLockControllerFactory);
@@ -338,6 +350,32 @@
return id;
}
+ private void rapidFire(Runnable... tasks) {
+ final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
+ final CountDownLatch latch = new CountDownLatch(tasks.length);
+ for (int i = 0; i < tasks.length; i++) {
+ final Runnable task = tasks[i];
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ barrier.await();
+ task.run();
+ } catch (InterruptedException | BrokenBarrierException e){
+ Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
+ } finally {
+ latch.countDown();
+ }
+ }
+ }).start();
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
+ }
+ }
+
// A simple outgoing call, verifying that the appropriate connection service is contacted,
// the proper lifecycle is followed, and both In-Call Services are updated correctly.
public void testSingleOutgoingCall() throws Exception {
@@ -398,4 +436,42 @@
assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState());
assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState());
}
+
+ public void testDeadlockOnOutgoingCall() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ TelecomSystemTest test = new TelecomSystemTest();
+ test.setContext(getContext());
+ test.setTestContext(getTestContext());
+ test.setName(getName());
+ test.setUp();
+ test.do_testDeadlockOnOutgoingCall();
+ test.tearDown();
+ }
+ }
+
+ public void do_testDeadlockOnOutgoingCall() throws Exception {
+ final String connectionId = startOutgoingPhoneCall(
+ "650-555-1212",
+ mPhoneAccountA0.getAccountHandle(),
+ mConnectionServiceFixtureA);
+ rapidFire(
+ new Runnable() {
+ @Override
+ public void run() {
+ while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
+ mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
+ }
+ }
+ },
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mConnectionServiceFixtureA.sendSetActive(connectionId);
+ } catch (Exception e) {
+ Log.e(this, e, "");
+ }
+ }
+ });
+ }
}