Hookup incoming and outgoing SIP calls
This CL hooks up SipConnection and SipConnectionService.
Bug: 14999064
Change-Id: Iddf4a06c70fb73398844e48a80be4c63f6b82e73
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4279616..f54b63a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -466,9 +466,16 @@
</intent-filter>
</activity>
- <service android:singleUser="true"
- android:name="com.android.services.telephony.sip.SipConnectionService"
- android:label="@string/sip_connection_service_label">
+ <!-- Start SIP -->
+ <service android:name="com.android.services.telephony.sip.SipCallServiceProvider"
+ android:singleUser="true" >
+ <intent-filter>
+ <action android:name="android.telecomm.CallServiceProvider" />
+ </intent-filter>
+ </service>
+ <service android:name="com.android.services.telephony.sip.SipConnectionService"
+ android:label="@string/sip_connection_service_label"
+ android:singleUser="true" >
<intent-filter>
<action android:name="android.telecomm.ConnectionService" />
</intent-filter>
@@ -497,13 +504,15 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:uiOptions="splitActionBarWhenNarrow">
</activity>
- <activity android:name="com.android.services.telephony.SipCallOptionHandler"
+ <activity android:name="com.android.services.telephony.sip.SipProfileChooserDialogs"
android:theme="@style/SipCallOptionHandlerTheme"
android:screenOrientation="nosensor"
android:configChanges="orientation|screenSize|keyboardHidden"
android:excludeFromRecents="true">
</activity>
+ <!-- End SIP -->
+
<activity android:name="ErrorDialogActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:excludeFromRecents="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0e0042b..aa60664 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1306,15 +1306,6 @@
Do not translate. -->
<string name="call_settings_title_font_family">sans-serif-light</string>
- <!-- Title of the dialog to choose WiFi calling. -->
- <string name ="choose_wifi_for_call_msg">Use Wi-Fi to place call?</string>
-
- <!-- Title for button to use WiFi calling. -->
- <string name ="choose_wifi_for_call_yes">Yes</string>
-
- <!-- Title for button to not use WiFi calling. -->
- <string name ="choose_wifi_for_call_no">No</string>
-
<!-- Label for PSTN connection service. -->
<string name="pstn_connection_service_label">Built-in SIM cards</string>
diff --git a/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
index b949351..62b8266 100644
--- a/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
@@ -16,18 +16,15 @@
package com.android.services.telephony.sip;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.sip.SipPhone;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.net.sip.SipAudioCall;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.telecomm.TelecommConstants;
import android.util.Log;
import java.util.List;
@@ -59,7 +56,17 @@
private void takeCall(Context context, Intent intent) {
if (VERBOSE) log("takeCall, intent: " + intent);
- // TODO(sail): Add support for incoming SIP calls.
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(SipUtil.EXTRA_INCOMING_CALL_INTENT, intent);
+
+ Intent telecommIntent = new Intent(TelecommConstants.ACTION_INCOMING_CALL);
+ telecommIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ telecommIntent.putExtra(TelecommConstants.EXTRA_CALL_SERVICE_DESCRIPTOR,
+ SipCallServiceProvider.getDescriptor(context));
+ telecommIntent.putExtra(TelecommConstants.EXTRA_INCOMING_CALL_EXTRAS, extras);
+
+ context.startActivityAsUser(telecommIntent, UserHandle.CURRENT);
}
private void registerAllProfiles(final Context context) {
diff --git a/sip/src/com/android/services/telephony/sip/SipCallServiceProvider.java b/sip/src/com/android/services/telephony/sip/SipCallServiceProvider.java
new file mode 100644
index 0000000..cc467e9
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/SipCallServiceProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.services.telephony.sip;
+
+import android.content.Context;
+import android.telecomm.CallServiceDescriptor;
+import android.telecomm.CallServiceLookupResponse;
+import android.telecomm.CallServiceProvider;
+
+import java.util.ArrayList;
+
+public class SipCallServiceProvider extends CallServiceProvider {
+ @Override
+ public void lookupCallServices(CallServiceLookupResponse response) {
+ ArrayList<CallServiceDescriptor> descriptors = new ArrayList<CallServiceDescriptor>();
+ descriptors.add(getDescriptor(this));
+ response.setCallServiceDescriptors(descriptors);
+ }
+
+ static CallServiceDescriptor getDescriptor(Context context) {
+ return CallServiceDescriptor.newBuilder(context)
+ .setConnectionService(SipConnectionService.class)
+ .setNetworkType(CallServiceDescriptor.FLAG_WIFI | CallServiceDescriptor.FLAG_MOBILE)
+ .build();
+ }
+}
diff --git a/sip/src/com/android/services/telephony/sip/SipConnection.java b/sip/src/com/android/services/telephony/sip/SipConnection.java
index 40239a4..c6e1be6 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnection.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnection.java
@@ -16,26 +16,54 @@
package com.android.services.telephony.sip;
+import android.os.Handler;
+import android.os.Message;
import android.telecomm.CallAudioState;
+import android.telecomm.CallCapabilities;
import android.telecomm.Connection;
import android.util.Log;
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.sip.SipPhone;
+
import java.util.List;
-public class SipConnection extends Connection {
+final class SipConnection extends Connection {
private static final String PREFIX = "[SipConnection] ";
private static final boolean VERBOSE = true; /* STOP SHIP if true */
- private final com.android.internal.telephony.Connection mConnection;
+ private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
- public SipConnection(com.android.internal.telephony.Connection connection) {
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PRECISE_CALL_STATE_CHANGED:
+ updateState();
+ break;
+ }
+ }
+ };
+
+ private com.android.internal.telephony.Connection mOriginalConnection;
+ private Call.State mOriginalConnectionState = Call.State.IDLE;
+
+ SipConnection(com.android.internal.telephony.Connection connection) {
if (VERBOSE) log("new SipConnection, connection: " + connection);
- mConnection = connection;
+ mOriginalConnection = connection;
+ if (getPhone() != null) {
+ getPhone().registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED,
+ null);
+ }
}
@Override
protected void onSetAudioState(CallAudioState state) {
if (VERBOSE) log("onSetAudioState: " + state);
+ if (getPhone() != null) {
+ getPhone().setEchoSuppressionEnabled();
+ }
}
@Override
@@ -46,51 +74,103 @@
@Override
protected void onPlayDtmfTone(char c) {
if (VERBOSE) log("onPlayDtmfTone");
+ if (getPhone() != null) {
+ getPhone().startDtmf(c);
+ }
}
@Override
protected void onStopDtmfTone() {
if (VERBOSE) log("onStopDtmfTone");
+ if (getPhone() != null) {
+ getPhone().stopDtmf();
+ }
}
@Override
protected void onDisconnect() {
if (VERBOSE) log("onDisconnect");
+ try {
+ if (getCall() != null && !getCall().isMultiparty()) {
+ getCall().hangup();
+ } else if (mOriginalConnection != null) {
+ mOriginalConnection.hangup();
+ }
+ } catch (CallStateException e) {
+ log("onDisconnect, exception: " + e);
+ }
}
@Override
protected void onSeparate() {
if (VERBOSE) log("onSeparate");
+ try {
+ if (mOriginalConnection != null) {
+ mOriginalConnection.separate();
+ }
+ } catch (CallStateException e) {
+ log("onSeparate, exception: " + e);
+ }
}
@Override
protected void onAbort() {
if (VERBOSE) log("onAbort");
+ onDisconnect();
}
@Override
protected void onHold() {
if (VERBOSE) log("onHold");
+ try {
+ if (getPhone() != null && getState() == State.ACTIVE) {
+ getPhone().switchHoldingAndActive();
+ }
+ } catch (CallStateException e) {
+ log("onHold, exception: " + e);
+ }
}
@Override
protected void onUnhold() {
if (VERBOSE) log("onUnhold");
+ try {
+ if (getPhone() != null && getState() == State.HOLDING) {
+ getPhone().switchHoldingAndActive();
+ }
+ } catch (CallStateException e) {
+ log("onUnhold, exception: " + e);
+ }
}
@Override
protected void onAnswer() {
if (VERBOSE) log("onAnswer");
+ try {
+ if (isValidRingingCall() && getPhone() != null) {
+ getPhone().acceptCall();
+ }
+ } catch (CallStateException e) {
+ log("onAnswer, exception: " + e);
+ }
}
@Override
protected void onReject() {
if (VERBOSE) log("onReject");
+ try {
+ if (isValidRingingCall() && getPhone() != null) {
+ getPhone().rejectCall();
+ }
+ } catch (CallStateException e) {
+ log("onReject, exception: " + e);
+ }
}
@Override
protected void onPostDialContinue(boolean proceed) {
if (VERBOSE) log("onPostDialContinue, proceed: " + proceed);
+ // SIP doesn't have post dial support.
}
@Override
@@ -103,6 +183,93 @@
if (VERBOSE) log("onPhoneAccountClicked");
}
+ private Call getCall() {
+ if (mOriginalConnection != null) {
+ return mOriginalConnection.getCall();
+ }
+ return null;
+ }
+
+ SipPhone getPhone() {
+ Call call = getCall();
+ if (call != null) {
+ return (SipPhone) call.getPhone();
+ }
+ return null;
+ }
+
+ private boolean isValidRingingCall() {
+ Call call = getCall();
+ return call != null && call.getState().isRinging() &&
+ call.getEarliestConnection() == mOriginalConnection;
+ }
+
+ private void updateState() {
+ if (mOriginalConnection == null) {
+ return;
+ }
+
+ Call.State newState = mOriginalConnection.getState();
+ if (VERBOSE) log("updateState, " + mOriginalConnectionState + " -> " + newState);
+ if (mOriginalConnectionState != newState) {
+ mOriginalConnectionState = newState;
+ switch (newState) {
+ case IDLE:
+ break;
+ case ACTIVE:
+ setActive();
+ break;
+ case HOLDING:
+ setOnHold();
+ break;
+ case DIALING:
+ case ALERTING:
+ setDialing();
+ break;
+ case INCOMING:
+ case WAITING:
+ setRinging();
+ break;
+ case DISCONNECTED:
+ setDisconnected(mOriginalConnection.getDisconnectCause(), null);
+ close();
+ break;
+ case DISCONNECTING:
+ break;
+ }
+ updateCallCapabilities();
+ }
+ }
+
+ private int buildCallCapabilities() {
+ int capabilities = CallCapabilities.MUTE | CallCapabilities.SUPPORT_HOLD;
+ if (getState() == State.ACTIVE || getState() == State.HOLDING) {
+ capabilities |= CallCapabilities.HOLD;
+ }
+ return capabilities;
+ }
+
+ void updateCallCapabilities() {
+ int newCallCapabilities = buildCallCapabilities();
+ if (getCallCapabilities() != newCallCapabilities) {
+ setCallCapabilities(newCallCapabilities);
+ }
+ }
+
+ void onAddedToCallService() {
+ if (VERBOSE) log("onAddedToCallService");
+ updateCallCapabilities();
+ setAudioModeIsVoip(true);
+ }
+
+ private void close() {
+ if (getPhone() != null) {
+ getPhone().unregisterForPreciseCallStateChanged(mHandler);
+ }
+ mOriginalConnection = null;
+ setDestroyed();
+ }
+
private static void log(String msg) {
Log.d(SipUtil.LOG_TAG, PREFIX + msg);
}
diff --git a/sip/src/com/android/services/telephony/sip/SipConnectionService.java b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
index 9b7b33f..1dc78e3 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnectionService.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
@@ -17,6 +17,8 @@
package com.android.services.telephony.sip;
import android.content.Context;
+import android.content.Intent;
+import android.net.sip.SipAudioCall;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
@@ -32,76 +34,106 @@
import android.util.Log;
import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.sip.SipPhone;
import java.util.HashMap;
-public class SipConnectionService extends ConnectionService {
+public final class SipConnectionService extends ConnectionService {
private static final String PREFIX = "[SipConnectionService] ";
private static final boolean VERBOSE = true; /* STOP SHIP if true */
- private class GetSipProfileTask extends AsyncTask<Void, Void, SipProfile> {
- private final ConnectionRequest mRequest;
- private final OutgoingCallResponse mResponse;
- private final SipProfileDb mSipProfileDb;
- private final SipSharedPreferences mSipSharedPreferences;
-
- GetSipProfileTask(
- Context context,
- ConnectionRequest request,
- OutgoingCallResponse response) {
- mRequest = request;
- mResponse = response;
- mSipProfileDb = new SipProfileDb(context);
- mSipSharedPreferences = new SipSharedPreferences(context);
- }
-
- @Override
- protected SipProfile doInBackground(Void... params) {
- String primarySipUri = mSipSharedPreferences.getPrimaryAccount();
- for (SipProfile profile : mSipProfileDb.retrieveSipProfileList()) {
- if (profile.getUriString().equals(primarySipUri)) {
- return profile;
- }
- }
- // TODO(sail): Handle non-primary profiles by showing dialog.
- return null;
- }
-
- @Override
- protected void onPostExecute(SipProfile profile) {
- onSipProfileChosen(profile, mRequest, mResponse);
- }
- }
-
@Override
protected void onCreateConnections(
- ConnectionRequest request,
- OutgoingCallResponse<Connection> callback) {
+ final ConnectionRequest request,
+ final OutgoingCallResponse<Connection> response) {
if (VERBOSE) log("onCreateConnections, request: " + request);
- new GetSipProfileTask(this, request, callback).execute();
+
+ SipProfileChooser.Callback callback = new SipProfileChooser.Callback() {
+ @Override
+ public void onSipChosen(SipProfile profile) {
+ if (VERBOSE) log("onCreateConnections, onSipChosen: " + profile);
+ SipConnection connection = createConnectionForProfile(profile, request);
+ if (connection == null) {
+ response.onCancel(request);
+ } else {
+ response.onSuccess(request, connection);
+ }
+ }
+
+ @Override
+ public void onSipNotChosen() {
+ if (VERBOSE) log("onCreateConnections, onSipNotChosen");
+ response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+ }
+
+ @Override
+ public void onCancelCall() {
+ if (VERBOSE) log("onCreateConnections, onCancelCall");
+ response.onCancel(request);
+ }
+ };
+
+ SipProfileChooser chooser = new SipProfileChooser(this, callback);
+ chooser.start(request.getHandle(), request.getExtras());
}
@Override
protected void onCreateConferenceConnection(
String token,
Connection connection,
- Response<String, Connection> callback) {
+ Response<String, Connection> response) {
if (VERBOSE) log("onCreateConferenceConnection, connection: " + connection);
}
@Override
protected void onCreateIncomingConnection(
ConnectionRequest request,
- Response<ConnectionRequest, Connection> callback) {
+ Response<ConnectionRequest, Connection> response) {
if (VERBOSE) log("onCreateIncomingConnection, request: " + request);
+
+ Intent sipIntent = (Intent) request.getExtras().getParcelable(
+ SipUtil.EXTRA_INCOMING_CALL_INTENT);
+ if (sipIntent == null) {
+ if (VERBOSE) log("onCreateIncomingConnection, no SIP intent");
+ response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+ return;
+ }
+
+ SipAudioCall sipAudioCall;
+ try {
+ sipAudioCall = SipManager.newInstance(this).takeAudioCall(sipIntent, null);
+ } catch (SipException e) {
+ log("onCreateConferenceConnection, takeAudioCall exception: " + e);
+ response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+ return;
+ }
+
+ SipPhone phone = findPhoneForProfile(sipAudioCall.getLocalProfile());
+ if (phone == null) {
+ phone = createPhoneForProfile(sipAudioCall.getLocalProfile());
+ }
+ if (phone != null) {
+ com.android.internal.telephony.Connection originalConnection = phone.takeIncomingCall(
+ sipAudioCall);
+ if (VERBOSE) log("onCreateIncomingConnection, new connection: " + originalConnection);
+ if (originalConnection != null) {
+ SipConnection connection = new SipConnection(originalConnection);
+ response.onResult(getConnectionRequestForIncomingCall(request, sipAudioCall),
+ connection);
+ } else {
+ if (VERBOSE) log("onCreateIncomingConnection, takingIncomingCall failed");
+ response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+ }
+ }
}
@Override
protected void onConnectionAdded(Connection connection) {
if (VERBOSE) log("onConnectionAdded, connection: " + connection);
+ if (connection instanceof SipConnection) {
+ ((SipConnection) connection).onAddedToCallService();
+ }
}
@Override
@@ -109,45 +141,71 @@
if (VERBOSE) log("onConnectionRemoved, connection: " + connection);
}
- private void onSipProfileChosen(
+ private SipConnection createConnectionForProfile(
SipProfile profile,
- ConnectionRequest request,
- OutgoingCallResponse response) {
- if (profile != null) {
- String sipUri = profile.getUriString();
- SipPhone phone = null;
- try {
- SipManager.newInstance(this).open(profile);
- phone = (SipPhone) PhoneFactory.makeSipPhone(sipUri);
- startCallWithPhone(phone, request, response);
- } catch (SipException e) {
- log("Failed to make a SIP phone: " + e);
- response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED,
- "Failed to make a SIP phone: " + e);
+ ConnectionRequest request) {
+ SipPhone phone = findPhoneForProfile(profile);
+ if (phone == null) {
+ phone = createPhoneForProfile(profile);
+ }
+ if (phone != null) {
+ return startCallWithPhone(phone, request);
+ }
+ return null;
+ }
+
+ private SipPhone findPhoneForProfile(SipProfile profile) {
+ if (VERBOSE) log("findPhoneForProfile, profile: " + profile);
+ for (Connection connection : getAllConnections()) {
+ if (connection instanceof SipConnection) {
+ SipPhone phone = ((SipConnection) connection).getPhone();
+ if (phone != null && phone.getSipUri().equals(profile.getUriString())) {
+ if (VERBOSE) log("findPhoneForProfile, found existing phone: " + phone);
+ return phone;
+ }
}
- } else {
- response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED,
- "Failed to find SIP profile");
+ }
+ if (VERBOSE) log("findPhoneForProfile, no phone found");
+ return null;
+ }
+
+ private SipPhone createPhoneForProfile(SipProfile profile) {
+ if (VERBOSE) log("createPhoneForProfile, profile: " + profile);
+ try {
+ SipManager.newInstance(this).open(profile);
+ return (SipPhone) PhoneFactory.makeSipPhone(profile.getUriString());
+ } catch (SipException e) {
+ log("createPhoneForProfile, exception: " + e);
+ return null;
}
}
- protected void startCallWithPhone(
- Phone phone,
- ConnectionRequest request,
- OutgoingCallResponse<Connection> response) {
+ private SipConnection startCallWithPhone(SipPhone phone, ConnectionRequest request) {
String number = request.getHandle().getSchemeSpecificPart();
+ if (VERBOSE) log("startCallWithPhone, number: " + number);
+
try {
- com.android.internal.telephony.Connection connection =
+ com.android.internal.telephony.Connection originalConnection =
phone.dial(number, request.getVideoState());
- SipConnection sipConnection = new SipConnection(connection);
- response.onSuccess(request, sipConnection);
+ return new SipConnection(originalConnection);
} catch (CallStateException e) {
- log("Call to Phone.dial failed with exception: " + e);
- response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED,
- "Call to Phone.dial failed with exception: " + e);
+ log("startCallWithPhone, exception: " + e);
+ return null;
}
}
+ private ConnectionRequest getConnectionRequestForIncomingCall(ConnectionRequest request,
+ SipAudioCall audioCall) {
+ SipProfile callee = audioCall.getPeerProfile();
+ String domain = callee.getSipDomain();
+ if (domain.endsWith(":5060")) {
+ domain = domain.substring(0, domain.length() - 5);
+ }
+ Uri uri = Uri.fromParts(SipUtil.SCHEME_SIP, callee.getUserName() + "@" + domain, null);
+ return new ConnectionRequest(request.getAccount(), request.getCallId(), uri,
+ request.getExtras(), 0);
+ }
+
private static void log(String msg) {
Log.d(SipUtil.LOG_TAG, PREFIX + msg);
}
diff --git a/sip/src/com/android/services/telephony/sip/SipEditor.java b/sip/src/com/android/services/telephony/sip/SipEditor.java
index a765296..2f2bdef 100644
--- a/sip/src/com/android/services/telephony/sip/SipEditor.java
+++ b/sip/src/com/android/services/telephony/sip/SipEditor.java
@@ -16,7 +16,6 @@
package com.android.services.telephony.sip;
-import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.services.telephony.sip.SipUtil;
@@ -75,7 +74,6 @@
private SipManager mSipManager;
private SipProfileDb mProfileDb;
private SipProfile mOldProfile;
- private CallManager mCallManager;
private Button mRemoveButton;
enum PreferenceKey {
@@ -146,7 +144,7 @@
public void onResume() {
super.onResume();
mHomeButtonClicked = false;
- if (mCallManager.getState() != PhoneConstants.State.IDLE) {
+ if (!SipUtil.isPhoneIdle(this)) {
mAdvancedSettings.show();
getPreferenceScreen().setEnabled(false);
if (mRemoveButton != null) mRemoveButton.setEnabled(false);
@@ -164,7 +162,6 @@
mSipManager = SipManager.newInstance(this);
mSharedPreferences = new SipSharedPreferences(this);
mProfileDb = new SipProfileDb(this);
- mCallManager = CallManager.getInstance();
setContentView(R.layout.sip_settings_ui);
addPreferencesFromResource(R.xml.sip_edit);
diff --git a/sip/src/com/android/services/telephony/sip/SipProfileChooser.java b/sip/src/com/android/services/telephony/sip/SipProfileChooser.java
new file mode 100644
index 0000000..687edfa
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/SipProfileChooser.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2014 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.services.telephony.sip;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.sip.SipProfile;
+import android.net.sip.SipManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.provider.Settings;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.List;
+
+/** Find a SIP profile to use for the an outgoing call. */
+final class SipProfileChooser {
+ private static final String PREFIX = "[SipProfileChooser] ";
+ private static final boolean VERBOSE = true; /* STOP SHIP if true */
+
+ interface Callback {
+ // Call will be completed by the given SIP profile.
+ void onSipChosen(SipProfile profile);
+ // Call will be tried by another connection service (GSM, CDMA, etc...).
+ void onSipNotChosen();
+ // Call will be aborted.
+ void onCancelCall();
+ }
+
+ private final Context mContext;
+ private final Callback mCallback;
+ private final SipProfileDb mSipProfileDb;
+ private List<SipProfile> mProfileList;
+ private SipProfile mPrimaryProfile;
+ private final Handler mHandler = new Handler();
+
+ SipProfileChooser(Context context, Callback callback) {
+ mContext = context;
+ mCallback = callback;
+ mSipProfileDb = new SipProfileDb(mContext);
+ }
+
+ void start(Uri handle, Bundle extras) {
+ if (VERBOSE) log("start, handle: " + handle);
+
+ String scheme = handle.getScheme();
+ if (!SipUtil.SCHEME_SIP.equals(scheme) && !SipUtil.SCHEME_TEL.equals(scheme)) {
+ if (VERBOSE) log("start, unknown scheme");
+ mCallback.onSipNotChosen();
+ return;
+ }
+
+ String phoneNumber = handle.getSchemeSpecificPart();
+ // Consider "tel:foo@exmaple.com" a SIP number.
+ boolean isSipNumber = SipUtil.SCHEME_SIP.equals(scheme) ||
+ PhoneNumberUtils.isUriNumber(phoneNumber);
+ if (!SipUtil.isVoipSupported(mContext)) {
+ if (isSipNumber) {
+ if (VERBOSE) log("start, VOIP not supported, dropping call");
+ SipProfileChooserDialogs.showNoVoip(mContext, new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int choice, Bundle resultData) {
+ mCallback.onCancelCall();
+ }
+ });
+ } else {
+ if (VERBOSE) log("start, VOIP not supported");
+ mCallback.onSipNotChosen();
+ }
+ return;
+ }
+
+ // Don't use SIP for numbers modified by a gateway.
+ if (extras != null && extras.getString(SipUtil.GATEWAY_PROVIDER_PACKAGE) != null) {
+ if (VERBOSE) log("start, not using SIP for numbers modified by gateway");
+ mCallback.onSipNotChosen();
+ return;
+ }
+
+ if (!isNetworkConnected()) {
+ if (isSipNumber) {
+ if (VERBOSE) log("start, network not connected, dropping call");
+ SipProfileChooserDialogs.showNoInternetError(mContext,
+ new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int choice, Bundle resultData) {
+ mCallback.onCancelCall();
+ }
+ });
+ } else {
+ if (VERBOSE) log("start, network not connected");
+ mCallback.onSipNotChosen();
+ }
+ return;
+ }
+
+ // Only ask user to pick SIP or Cell if they're actually connected to a cell network.
+ SipSharedPreferences sipPreferences = new SipSharedPreferences(mContext);
+ String callOption = sipPreferences.getSipCallOption();
+ if (callOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME) && !isSipNumber &&
+ isInCellNetwork()) {
+ if (VERBOSE) log("start, call option set to ask, asking");
+ ResultReceiver receiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int choice, Bundle resultData) {
+ if (choice == DialogInterface.BUTTON_NEGATIVE) {
+ mCallback.onCancelCall();
+ } else if (SipProfileChooserDialogs.isSelectedPhoneTypeSip(mContext,
+ choice)) {
+ buildProfileList();
+ } else {
+ mCallback.onSipNotChosen();
+ }
+ }
+ };
+ SipProfileChooserDialogs.showSelectPhoneType(mContext, receiver);
+ return;
+ }
+
+ if (callOption.equals(Settings.System.SIP_ADDRESS_ONLY) && !isSipNumber) {
+ if (VERBOSE) log("start, call option set to SIP only, not a sip number");
+ mCallback.onSipNotChosen();
+ return;
+ }
+
+ if ((mSipProfileDb.getProfilesCount() == 0) && !isSipNumber) {
+ if (VERBOSE) log("start, no SIP accounts and not sip number");
+ mCallback.onSipNotChosen();
+ return;
+ }
+
+ if (VERBOSE) log("start, building profile list");
+ buildProfileList();
+ }
+
+ private boolean isNetworkConnected() {
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ NetworkInfo ni = cm.getActiveNetworkInfo();
+ if (ni != null && ni.isConnected()) {
+ return ni.getType() == ConnectivityManager.TYPE_WIFI ||
+ !SipManager.isSipWifiOnly(mContext);
+ }
+ }
+ return false;
+ }
+
+ private boolean isInCellNetwork() {
+ TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ if (telephonyManager != null) {
+ // We'd also like to check the radio's power state but there's no public API to do this.
+ int phoneType = telephonyManager.getPhoneType();
+ return phoneType != TelephonyManager.PHONE_TYPE_NONE &&
+ phoneType != TelephonyManager.PHONE_TYPE_SIP;
+ }
+ return false;
+ }
+
+ private void buildProfileList() {
+ if (VERBOSE) log("buildProfileList");
+ final SipSharedPreferences preferences = new SipSharedPreferences(mContext);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ mProfileList = mSipProfileDb.retrieveSipProfileList();
+ if (mProfileList != null) {
+ String primarySipUri = preferences.getPrimaryAccount();
+ for (SipProfile profile : mProfileList) {
+ if (profile.getUriString().equals(primarySipUri)) {
+ mPrimaryProfile = profile;
+ break;
+ }
+ }
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onBuildProfileListDone();
+ }
+ });
+ }
+ }).start();
+ }
+
+ /**
+ * At this point we definitely want to make a SIP call, we just need to figure out which
+ * profile to use.
+ */
+ private void onBuildProfileListDone() {
+ if (VERBOSE) log("onBuildProfileListDone");
+ if (mProfileList == null || mProfileList.size() == 0) {
+ if (VERBOSE) log("onBuildProfileListDone, no profiles, showing settings dialog");
+ ResultReceiver receiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int choice, Bundle resultData) {
+ if (choice == DialogInterface.BUTTON_POSITIVE) {
+ openSipSettings();
+ }
+ mCallback.onCancelCall();
+ }
+ };
+ SipProfileChooserDialogs.showStartSipSettings(mContext, receiver);
+ } else if (mPrimaryProfile == null) {
+ if (VERBOSE) log("onBuildProfileListDone, no primary profile, showing select dialog");
+ ResultReceiver receiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int choice, Bundle resultData) {
+ if (choice >= 0 && choice < mProfileList.size()) {
+ SipProfile profile = mProfileList.get(choice);
+ if (SipProfileChooserDialogs.shouldMakeSelectedProflePrimary(mContext,
+ resultData)) {
+ SipSharedPreferences pref = new SipSharedPreferences(mContext);
+ pref.setPrimaryAccount(profile.getUriString());
+ }
+ mCallback.onSipChosen(profile);
+ } else {
+ mCallback.onCancelCall();
+ }
+ }
+ };
+ SipProfileChooserDialogs.showSelectProfile(mContext, mProfileList, receiver);
+ } else {
+ mCallback.onSipChosen(mPrimaryProfile);
+ }
+ }
+
+ private void openSipSettings() {
+ Intent newIntent = new Intent(mContext, SipSettings.class);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(newIntent);
+ }
+
+ private static void log(String msg) {
+ Log.d(SipUtil.LOG_TAG, PREFIX + msg);
+ }
+}
diff --git a/sip/src/com/android/services/telephony/sip/SipProfileChooserDialogs.java b/sip/src/com/android/services/telephony/sip/SipProfileChooserDialogs.java
new file mode 100644
index 0000000..3ba633e
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/SipProfileChooserDialogs.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 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.services.telephony.sip;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import java.util.List;
+
+public final class SipProfileChooserDialogs extends Activity
+ implements DialogInterface.OnClickListener,
+ DialogInterface.OnCancelListener, CompoundButton.OnCheckedChangeListener {
+ private static final String PREFIX = "[SipProfileChooserDialogs] ";
+ private static final boolean VERBOSE = true; /* STOP SHIP if true */
+
+ private static final String EXTRA_RESULT_RECEIVER = "result_receiver";
+ private static final String EXTRA_DIALOG_ID = "dialog_id";
+ private static final String EXTRA_PROFILE_NAMES = "profile_names";
+ private static final String EXTRA_MAKE_PRIMARY = "make_primary";
+
+ private static final int DIALOG_SELECT_PHONE_TYPE = 0;
+ private static final int DIALOG_SELECT_PROFILE = 1;
+ private static final int DIALOG_START_SIP_SETTINGS = 2;
+ private static final int DIALOG_NO_INTERNET_ERROR = 3;
+ private static final int DIALOG_NO_VOIP = 4;
+
+ private TextView mUnsetPriamryHint;
+ private boolean mMakePrimary;
+
+ static void showSelectPhoneType(Context context, ResultReceiver resultReceiver) {
+ show(context, DIALOG_SELECT_PHONE_TYPE, null, resultReceiver);
+ }
+
+ static void showSelectProfile(Context context, List<SipProfile> profiles,
+ ResultReceiver resultReceiver) {
+ show(context, DIALOG_SELECT_PROFILE, profiles, resultReceiver);
+ }
+
+ static void showStartSipSettings(Context context, ResultReceiver resultReceiver) {
+ show(context, DIALOG_START_SIP_SETTINGS, null, resultReceiver);
+ }
+
+ static void showNoInternetError(Context context, ResultReceiver resultReceiver) {
+ show(context, DIALOG_NO_INTERNET_ERROR, null, resultReceiver);
+ }
+
+ static void showNoVoip(Context context, ResultReceiver resultReceiver) {
+ show(context, DIALOG_NO_VOIP, null, resultReceiver);
+ }
+
+ static boolean isSelectedPhoneTypeSip(Context context, int choice) {
+ String[] phoneTypes = context.getResources().getStringArray(R.array.phone_type_values);
+ if (choice >= 0 && choice < phoneTypes.length) {
+ return phoneTypes[choice].equals(context.getString(R.string.internet_phone));
+ }
+ return false;
+ }
+
+ static boolean shouldMakeSelectedProflePrimary(Context context, Bundle extras) {
+ return extras.getBoolean(EXTRA_MAKE_PRIMARY);
+ }
+
+ static private void show(final Context context, final int dialogId,
+ final List<SipProfile> profiles,
+ final ResultReceiver resultReceiver) {
+ if (VERBOSE) log("show, starting delayed show, dialogId: " + dialogId);
+
+ // Wait for 1 second before showing the dialog. The sometimes prevents the InCallUI from
+ // popping up on top of the dialog. See http://b/16184268
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (VERBOSE) log("show, starting activity");
+ Intent intent = new Intent(context, SipProfileChooserDialogs.class)
+ .putExtra(EXTRA_RESULT_RECEIVER, resultReceiver)
+ .putExtra(EXTRA_DIALOG_ID, dialogId)
+ .putExtra(EXTRA_PROFILE_NAMES, getProfileNames(profiles))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ context.startActivity(intent);
+ }
+ }, 1000);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int dialogId = getIntent().getIntExtra(EXTRA_DIALOG_ID, 0);
+ if (VERBOSE) log("onCreate, dialogId: " + dialogId);
+
+ // Allow this activity to be visible in front of the keyguard. (This is only necessary for
+ // obscure scenarios like the user initiating a call and then immediately pressing the Power
+ // button.)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+
+ showDialog(dialogId);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle args) {
+ Dialog dialog;
+ switch(id) {
+ case DIALOG_SELECT_PHONE_TYPE:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.pick_outgoing_call_phone_type)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setSingleChoiceItems(R.array.phone_type_values, -1, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_SELECT_PROFILE:
+ String[] profileNames = getIntent().getStringArrayExtra(EXTRA_PROFILE_NAMES);
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.pick_outgoing_sip_phone)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setSingleChoiceItems(profileNames, -1, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ addMakeDefaultCheckBox(dialog);
+ break;
+ case DIALOG_START_SIP_SETTINGS:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_sip_account_found_title)
+ .setMessage(R.string.no_sip_account_found)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(R.string.sip_menu_add, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_NO_INTERNET_ERROR:
+ boolean wifiOnly = SipManager.isSipWifiOnly(this);
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(wifiOnly ? R.string.no_wifi_available_title
+ : R.string.no_internet_available_title)
+ .setMessage(wifiOnly ? R.string.no_wifi_available
+ : R.string.no_internet_available)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_NO_VOIP:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_voip)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ default:
+ dialog = null;
+ }
+ if (dialog != null) {
+ //mDialogs[id] = dialog;
+ }
+ return dialog;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (VERBOSE) log("onPause");
+ }
+
+ @Override
+ public void finish() {
+ if (VERBOSE) log("finish");
+ super.finish();
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (VERBOSE) log("onCheckedChanged, isChecked: " + isChecked);
+ mMakePrimary = isChecked;
+ if (isChecked) {
+ mUnsetPriamryHint.setVisibility(View.VISIBLE);
+ } else {
+ mUnsetPriamryHint.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ if (VERBOSE) log("onClick, id: " + id);
+ onChoiceMade(id);
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (VERBOSE) log("onCancel");
+ onChoiceMade(DialogInterface.BUTTON_NEGATIVE);
+ }
+
+ private void onChoiceMade(int choice) {
+ ResultReceiver resultReceiver = getIntent().getParcelableExtra(EXTRA_RESULT_RECEIVER);
+ if (resultReceiver != null) {
+ Bundle extras = new Bundle();
+ extras.putBoolean(EXTRA_MAKE_PRIMARY, mMakePrimary);
+ resultReceiver.send(choice, extras);
+ }
+ finish();
+ }
+
+ private void addMakeDefaultCheckBox(Dialog dialog) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ CheckBox makePrimaryCheckBox =
+ (CheckBox) view.findViewById(com.android.internal.R.id.alwaysUse);
+ makePrimaryCheckBox.setText(R.string.remember_my_choice);
+ makePrimaryCheckBox.setOnCheckedChangeListener(this);
+ mUnsetPriamryHint = (TextView)view.findViewById(com.android.internal.R.id.clearDefaultHint);
+ mUnsetPriamryHint.setText(R.string.reset_my_choice_hint);
+ mUnsetPriamryHint.setVisibility(View.GONE);
+ ((AlertDialog)dialog).setView(view);
+ }
+
+ static private String[] getProfileNames(List<SipProfile> profiles) {
+ if (profiles == null) {
+ return null;
+ }
+
+ String[] entries = new String[profiles.size()];
+ int i = 0;
+ for (SipProfile p : profiles) {
+ entries[i++] = p.getProfileName();
+ }
+ return entries;
+ }
+
+ private static void log(String msg) {
+ Log.d(SipUtil.LOG_TAG, PREFIX + msg);
+ }
+}
diff --git a/sip/src/com/android/services/telephony/sip/SipProfileDb.java b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
index bf4b6bb..7533d16 100644
--- a/sip/src/com/android/services/telephony/sip/SipProfileDb.java
+++ b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
@@ -35,7 +35,7 @@
/**
* Utility class that helps perform operations on the SipProfile database.
*/
-public class SipProfileDb {
+class SipProfileDb {
private static final String PREFIX = "[SipProfileDb] ";
private static final boolean VERBOSE = true; /* STOP SHIP if true */
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index cf35e31..e4fca2b 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -16,7 +16,6 @@
package com.android.services.telephony.sip;
-import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -73,7 +72,6 @@
private PackageManager mPackageManager;
private SipManager mSipManager;
- private CallManager mCallManager;
private SipProfileDb mProfileDb;
private SipProfile mProfile; // profile that's being edited
@@ -154,7 +152,6 @@
addPreferencesFromResource(R.xml.sip_setting);
mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST);
registerForReceiveCallsCheckBox();
- mCallManager = CallManager.getInstance();
updateProfilesStatus();
@@ -168,11 +165,7 @@
public void onResume() {
super.onResume();
- if (mCallManager.getState() != PhoneConstants.State.IDLE) {
- mButtonSipReceiveCalls.setEnabled(false);
- } else {
- mButtonSipReceiveCalls.setEnabled(true);
- }
+ mButtonSipReceiveCalls.setEnabled(SipUtil.isPhoneIdle(this));
}
@Override
@@ -489,8 +482,7 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(MENU_ADD_ACCOUNT).setEnabled(
- mCallManager.getState() == PhoneConstants.State.IDLE);
+ menu.findItem(MENU_ADD_ACCOUNT).setEnabled(SipUtil.isPhoneIdle(this));
return super.onPrepareOptionsMenu(menu);
}
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index 39b5655..7a1fcf7 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -20,9 +20,16 @@
import android.content.Context;
import android.content.Intent;
import android.net.sip.SipManager;
+import android.telecomm.TelecommManager;
public class SipUtil {
- public static final String LOG_TAG = "SIP";
+ static final String LOG_TAG = "SIP";
+ static final String EXTRA_INCOMING_CALL_INTENT =
+ "com.android.services.telephony.sip.incoming_call_intent";
+ static final String GATEWAY_PROVIDER_PACKAGE =
+ "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
+ static final String SCHEME_TEL = "tel";
+ static final String SCHEME_SIP = "sip";
private static boolean sIsVoipSupported;
private static boolean sIsVoipSupportedInitialized;
@@ -41,10 +48,19 @@
return sIsVoipSupported;
}
- public static PendingIntent createIncomingCallPendingIntent(Context context) {
+ static PendingIntent createIncomingCallPendingIntent(Context context) {
Intent intent = new Intent(context, SipBroadcastReceiver.class);
intent.setAction(SipManager.ACTION_SIP_INCOMING_CALL);
return PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
+
+ static boolean isPhoneIdle(Context context) {
+ TelecommManager manager = (TelecommManager) context.getSystemService(
+ Context.TELECOMM_SERVICE);
+ if (manager != null) {
+ return !manager.isInAPhoneCall();
+ }
+ return true;
+ }
}