From 788959e2d798da2d8a34cf89779421966d200f3d Mon Sep 17 00:00:00 2001 From: Sailesh Nepal Date: Tue, 8 Jul 2014 23:36:40 -0700 Subject: SIP Part 1, move sip code This CL moves the SIP code to sip/src/com/android/services/telephony/sip Moving the SIP code helps with a couple of things: - remove legacy dependency on old telephony code (PhoneUtils, etc...) - separate SIP from the PSTN connection code which is very different from SIP code - get the code ready for a future move out of Telephony all together Bug: 14999064 Change-Id: Id32de6517d31be4aa177b2764d5bac1e1f9851c2 --- Android.mk | 9 +- AndroidManifest.xml | 28 +- res/layout/sip_settings_ui.xml | 33 -- res/values/array.xml | 52 -- res/values/strings.xml | 198 ------- res/xml/sip_advanced_edit.xml | 74 --- res/xml/sip_edit.xml | 59 -- res/xml/sip_setting.xml | 26 - res/xml/sip_settings_category.xml | 48 -- sip/Android.mk | 31 + sip/AndroidManifest.xml | 22 + sip/proguard.flags | 3 + sip/res/layout/sip_settings_ui.xml | 33 ++ sip/res/values/array.xml | 52 ++ sip/res/values/strings.xml | 210 +++++++ sip/res/xml/sip_advanced_edit.xml | 74 +++ sip/res/xml/sip_edit.xml | 59 ++ sip/res/xml/sip_setting.xml | 26 + sip/res/xml/sip_settings_category.xml | 48 ++ .../telephony/sip/SipBroadcastReceiver.java | 97 +++ .../services/telephony/sip/SipConnection.java | 109 ++++ .../telephony/sip/SipConnectionService.java | 154 +++++ .../android/services/telephony/sip/SipEditor.java | 660 +++++++++++++++++++++ .../services/telephony/sip/SipProfileDb.java | 146 +++++ .../services/telephony/sip/SipSettings.java | 512 ++++++++++++++++ .../telephony/sip/SipSharedPreferences.java | 114 ++++ .../android/services/telephony/sip/SipUtil.java | 50 ++ src/com/android/phone/CallFeaturesSetting.java | 8 +- src/com/android/phone/OutgoingCallBroadcaster.java | 30 +- src/com/android/phone/PhoneUtils.java | 16 - src/com/android/phone/SipBroadcastReceiver.java | 145 ----- src/com/android/phone/SipCallOptionHandler.java | 499 ---------------- src/com/android/phone/SipUtil.java | 35 -- src/com/android/phone/sip/SipEditor.java | 657 -------------------- src/com/android/phone/sip/SipProfileDb.java | 144 ----- src/com/android/phone/sip/SipSettings.java | 514 ---------------- .../android/phone/sip/SipSharedPreferences.java | 109 ---- .../android/services/telephony/SipConnection.java | 79 --- .../services/telephony/SipConnectionService.java | 169 ------ .../telephony/TelephonyCallServiceProvider.java | 5 - 40 files changed, 2424 insertions(+), 2913 deletions(-) delete mode 100644 res/layout/sip_settings_ui.xml delete mode 100644 res/values/array.xml delete mode 100644 res/xml/sip_advanced_edit.xml delete mode 100644 res/xml/sip_edit.xml delete mode 100644 res/xml/sip_setting.xml delete mode 100644 res/xml/sip_settings_category.xml create mode 100644 sip/Android.mk create mode 100644 sip/AndroidManifest.xml create mode 100644 sip/proguard.flags create mode 100644 sip/res/layout/sip_settings_ui.xml create mode 100644 sip/res/values/array.xml create mode 100644 sip/res/values/strings.xml create mode 100644 sip/res/xml/sip_advanced_edit.xml create mode 100644 sip/res/xml/sip_edit.xml create mode 100644 sip/res/xml/sip_setting.xml create mode 100644 sip/res/xml/sip_settings_category.xml create mode 100644 sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java create mode 100644 sip/src/com/android/services/telephony/sip/SipConnection.java create mode 100644 sip/src/com/android/services/telephony/sip/SipConnectionService.java create mode 100644 sip/src/com/android/services/telephony/sip/SipEditor.java create mode 100644 sip/src/com/android/services/telephony/sip/SipProfileDb.java create mode 100644 sip/src/com/android/services/telephony/sip/SipSettings.java create mode 100644 sip/src/com/android/services/telephony/sip/SipSharedPreferences.java create mode 100644 sip/src/com/android/services/telephony/sip/SipUtil.java delete mode 100644 src/com/android/phone/SipBroadcastReceiver.java delete mode 100644 src/com/android/phone/SipCallOptionHandler.java delete mode 100644 src/com/android/phone/SipUtil.java delete mode 100644 src/com/android/phone/sip/SipEditor.java delete mode 100644 src/com/android/phone/sip/SipProfileDb.java delete mode 100644 src/com/android/phone/sip/SipSettings.java delete mode 100644 src/com/android/phone/sip/SipSharedPreferences.java delete mode 100644 src/com/android/services/telephony/SipConnection.java delete mode 100644 src/com/android/services/telephony/SipConnectionService.java diff --git a/Android.mk b/Android.mk index 75c81ac58..586709f47 100644 --- a/Android.mk +++ b/Android.mk @@ -6,8 +6,8 @@ include $(CLEAR_VARS) phone_common_dir := ../../apps/PhoneCommon -src_dirs := src $(phone_common_dir)/src -res_dirs := res $(phone_common_dir)/res +src_dirs := src $(phone_common_dir)/src sip/src +res_dirs := res $(phone_common_dir)/res sip/res LOCAL_JAVA_LIBRARIES := telephony-common voip-common ims-common LOCAL_STATIC_JAVA_LIBRARIES := \ @@ -23,14 +23,15 @@ LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ - --extra-packages com.android.phone.common + --extra-packages com.android.phone.common \ + --extra-packages com.android.services.telephony.sip LOCAL_PACKAGE_NAME := TeleService LOCAL_CERTIFICATE := platform LOCAL_PRIVILEGED_MODULE := true -LOCAL_PROGUARD_FLAG_FILES := proguard.flags +LOCAL_PROGUARD_FLAG_FILES := proguard.flags sip/proguard.flags include $(BUILD_PACKAGE) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 83f9f2046..427961607 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -466,32 +466,38 @@ - + + + + + + - - + android:uiOptions="splitActionBarWhenNarrow" + android:parentActivityName="com.android.phone.CallFeaturesSetting" > - - - - - - - - - - - - - diff --git a/res/values/array.xml b/res/values/array.xml deleted file mode 100644 index 245c09b26..000000000 --- a/res/values/array.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - UDP - TCP - - - - @string/sip_call_options_entry_1 - @string/sip_call_options_entry_2 - @string/sip_call_options_entry_3 - - - - @string/sip_call_options_wifi_only_entry_1 - @string/sip_call_options_entry_2 - @string/sip_call_options_entry_3 - - - - @string/sip_always - @string/sip_address_only - @string/sip_ask_me_each_time - - - - @string/pstn_phone - @string/internet_phone - - - - @string/sip_system_decide - @string/sip_always_send_keepalive - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index f9ff5b8d3..0e0042b0c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -914,9 +914,6 @@ Unable to release call(s). - - Internet call - Emergency call @@ -1132,199 +1129,6 @@ select contact - - - - - Internet call settings - - Internet calling (SIP) accounts - - Accounts - - Receive incoming calls - - Reduces battery life - - Use Internet calling - - Use Internet calling (Wi-Fi only) - - - For all calls when data network is available - - Only for Internet calls - - Ask for each call - - For all calls - - - Place call - - - Use Internet calling account: - - - Always use to place Internet calls - - - You can change which Internet calling account to use by default from the Phone > Settings > Internet call settings > Accounts screen. - - - Cell phone call - - - Internet call - - - No Internet calling account - - There are no Internet calling accounts on this phone. Do you want to add one now? - - Add - - - Add account - - Remove account - - SIP accounts - - Saving the account\u2026 - - Removing the account\u2026 - - - Save - - Discard - - - Close the profile - - OK - - Close - - - Primary account. %s - - Checking status... - - Registering\u2026 - - Still trying\u2026 - - Not receiving calls. - - Account registration stopped because there is no Internet connection. - - Account registration stopped because there is no Wi-Fi connection. - - Account registration unsuccessful. - - Receiving calls. - - Account registration unsuccessful: (%s); will try later - - Account registration unsuccessful: Incorrect username or password. - - Account registration unsuccessful: Check the server name. - - This account is currently in use by the %s app. - - - - SIP account details - - SIP account details - - - Server - - Username - - Password - - Display name - - Outbound proxy address - - Port number - - Transport type - - Send keep-alive - - Set as primary account - - Used for outbound calls - - Optional settings - - Authentication username - - Username used for authentication - - - <Not set> - - <Same as username> - - <Optional> - - - 5060 - - UDP - - - - ▷ Touch to show all - - ▽ Touch to hide all - - - %s is required and can\'t be left blank. - - Port number should be within 1000 and 65534. - - - No Internet connection - - No Wi-Fi connection - - To place an Internet call, check your Internet connection first. - - You need to be connected to a Wi-Fi network for Internet calls (use the Wireless & Network settings). - - Internet calling not supported - - - Automatic - - Always send - - - SIP_ALWAYS - SIP_ADDRESS_ONLY - SIP_ASK_ME_EACH_TIME - - - SipDomain - UserName - Password - DisplayName - ProxyAddress - Port - Protocol - SendKeepAlive - SetPrimary - advanced settings container - AuthUserName - - Voice calling not supported @@ -1514,6 +1318,4 @@ Built-in SIM cards - - Built-in Internet calling diff --git a/res/xml/sip_advanced_edit.xml b/res/xml/sip_advanced_edit.xml deleted file mode 100644 index b7974bacc..000000000 --- a/res/xml/sip_advanced_edit.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/res/xml/sip_edit.xml b/res/xml/sip_edit.xml deleted file mode 100644 index f091fc179..000000000 --- a/res/xml/sip_edit.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/res/xml/sip_setting.xml b/res/xml/sip_setting.xml deleted file mode 100644 index c75440485..000000000 --- a/res/xml/sip_setting.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/res/xml/sip_settings_category.xml b/res/xml/sip_settings_category.xml deleted file mode 100644 index e857be686..000000000 --- a/res/xml/sip_settings_category.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/sip/Android.mk b/sip/Android.mk new file mode 100644 index 000000000..cefb86d0b --- /dev/null +++ b/sip/Android.mk @@ -0,0 +1,31 @@ +# Copyright 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res) + +LOCAL_JAVA_LIBRARIES := telephony-common voip-common + +LOCAL_PACKAGE_NAME := com.android.services.telephony.sip + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +include $(BUILD_PACKAGE) +include $(CLEAR_VARS) diff --git a/sip/AndroidManifest.xml b/sip/AndroidManifest.xml new file mode 100644 index 000000000..0249abdf6 --- /dev/null +++ b/sip/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/sip/proguard.flags b/sip/proguard.flags new file mode 100644 index 000000000..23ae8c150 --- /dev/null +++ b/sip/proguard.flags @@ -0,0 +1,3 @@ +-verbose +-keep class com.android.services.telephony.sip.SipConnectionService +-keep class com.android.services.telephony.sip.SipBroadcastReceiver diff --git a/sip/res/layout/sip_settings_ui.xml b/sip/res/layout/sip_settings_ui.xml new file mode 100644 index 000000000..872c4b502 --- /dev/null +++ b/sip/res/layout/sip_settings_ui.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/sip/res/values/array.xml b/sip/res/values/array.xml new file mode 100644 index 000000000..245c09b26 --- /dev/null +++ b/sip/res/values/array.xml @@ -0,0 +1,52 @@ + + + + + + + UDP + TCP + + + + @string/sip_call_options_entry_1 + @string/sip_call_options_entry_2 + @string/sip_call_options_entry_3 + + + + @string/sip_call_options_wifi_only_entry_1 + @string/sip_call_options_entry_2 + @string/sip_call_options_entry_3 + + + + @string/sip_always + @string/sip_address_only + @string/sip_ask_me_each_time + + + + @string/pstn_phone + @string/internet_phone + + + + @string/sip_system_decide + @string/sip_always_send_keepalive + + + diff --git a/sip/res/values/strings.xml b/sip/res/values/strings.xml new file mode 100644 index 000000000..41499b25f --- /dev/null +++ b/sip/res/values/strings.xml @@ -0,0 +1,210 @@ + + + + + + + Internet call settings + + Internet calling (SIP) accounts + + Accounts + + Receive incoming calls + + Reduces battery life + + Use Internet calling + + Use Internet calling (Wi-Fi only) + + + For all calls when data network is available + + Only for Internet calls + + Ask for each call + + For all calls + + + Place call + + + Use Internet calling account: + + + Always use to place Internet calls + + + You can change which Internet calling account to use by default from the Phone > Settings > Internet call settings > Accounts screen. + + + Cell phone call + + + Internet call + + + No Internet calling account + + There are no Internet calling accounts on this phone. Do you want to add one now? + + Add + + + Add account + + Remove account + + SIP accounts + + Saving the account\u2026 + + Removing the account\u2026 + + + Save + + Discard + + + Close the profile + + OK + + Close + + + Primary account. %s + + Checking status... + + Registering\u2026 + + Still trying\u2026 + + Not receiving calls. + + Account registration stopped because there is no Internet connection. + + Account registration stopped because there is no Wi-Fi connection. + + Account registration unsuccessful. + + Receiving calls. + + Account registration unsuccessful: (%s); will try later + + Account registration unsuccessful: Incorrect username or password. + + Account registration unsuccessful: Check the server name. + + This account is currently in use by the %s app. + + + + SIP account details + + SIP account details + + + Server + + Username + + Password + + Display name + + Outbound proxy address + + Port number + + Transport type + + Send keep-alive + + Set as primary account + + Used for outbound calls + + Optional settings + + Authentication username + + Username used for authentication + + + <Not set> + + <Same as username> + + <Optional> + + + 5060 + + UDP + + + + ▷ Touch to show all + + ▽ Touch to hide all + + + %s is required and can\'t be left blank. + + Port number should be within 1000 and 65534. + + + No Internet connection + + No Wi-Fi connection + + To place an Internet call, check your Internet connection first. + + You need to be connected to a Wi-Fi network for Internet calls (use the Wireless & Network settings). + + Internet calling not supported + + + Automatic + + Always send + + + SIP_ALWAYS + SIP_ADDRESS_ONLY + SIP_ASK_ME_EACH_TIME + + + SipDomain + UserName + Password + DisplayName + ProxyAddress + Port + Protocol + SendKeepAlive + SetPrimary + advanced settings container + AuthUserName + + + Built-in Internet calling + diff --git a/sip/res/xml/sip_advanced_edit.xml b/sip/res/xml/sip_advanced_edit.xml new file mode 100644 index 000000000..b7974bacc --- /dev/null +++ b/sip/res/xml/sip_advanced_edit.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/sip/res/xml/sip_edit.xml b/sip/res/xml/sip_edit.xml new file mode 100644 index 000000000..f091fc179 --- /dev/null +++ b/sip/res/xml/sip_edit.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + diff --git a/sip/res/xml/sip_setting.xml b/sip/res/xml/sip_setting.xml new file mode 100644 index 000000000..c75440485 --- /dev/null +++ b/sip/res/xml/sip_setting.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/sip/res/xml/sip_settings_category.xml b/sip/res/xml/sip_settings_category.xml new file mode 100644 index 000000000..17ee577b1 --- /dev/null +++ b/sip/res/xml/sip_settings_category.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + diff --git a/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java new file mode 100644 index 000000000..b949351e8 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 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 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.util.Log; + +import java.util.List; + +/** + * Broadcast receiver that handles SIP-related intents. + */ +public class SipBroadcastReceiver extends BroadcastReceiver { + private static final String PREFIX = "[SipBroadcastReceiver] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + @Override + public void onReceive(Context context, final Intent intent) { + String action = intent.getAction(); + + if (!SipUtil.isVoipSupported(context)) { + if (VERBOSE) log("SIP VOIP not supported: " + action); + return; + } + + if (action.equals(SipManager.ACTION_SIP_INCOMING_CALL)) { + takeCall(context, intent); + } else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP)) { + registerAllProfiles(context); + } else { + if (VERBOSE) log("onReceive, action not processed: " + action); + } + } + + private void takeCall(Context context, Intent intent) { + if (VERBOSE) log("takeCall, intent: " + intent); + // TODO(sail): Add support for incoming SIP calls. + } + + private void registerAllProfiles(final Context context) { + if (VERBOSE) log("registerAllProfiles, start auto registration"); + final SipSharedPreferences sipSharedPreferences = new SipSharedPreferences(context); + new Thread(new Runnable() { + @Override + public void run() { + SipManager sipManager = SipManager.newInstance(context); + SipProfileDb profileDb = new SipProfileDb(context); + String primaryProfile = sipSharedPreferences.getPrimaryAccount(); + + List sipProfileList = profileDb.retrieveSipProfileList(); + + for (SipProfile profile : sipProfileList) { + boolean isPrimaryProfile = profile.getUriString().equals(primaryProfile); + if (profile.getAutoRegistration() || isPrimaryProfile) { + if (VERBOSE) log("registerAllProfiles, profile: " + profile); + try { + sipManager.open(profile, + SipUtil.createIncomingCallPendingIntent(context), null); + } catch (SipException e) { + log("registerAllProfiles, profile: " + profile.getProfileName() + + ", exception: " + e); + } + } + } + }} + ).start(); + } + + private static void log(String msg) { + Log.d(SipUtil.LOG_TAG, PREFIX + msg); + } +} diff --git a/sip/src/com/android/services/telephony/sip/SipConnection.java b/sip/src/com/android/services/telephony/sip/SipConnection.java new file mode 100644 index 000000000..40239a4d7 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipConnection.java @@ -0,0 +1,109 @@ +/* + * 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.telecomm.CallAudioState; +import android.telecomm.Connection; +import android.util.Log; + +import java.util.List; + +public 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; + + public SipConnection(com.android.internal.telephony.Connection connection) { + if (VERBOSE) log("new SipConnection, connection: " + connection); + mConnection = connection; + } + + @Override + protected void onSetAudioState(CallAudioState state) { + if (VERBOSE) log("onSetAudioState: " + state); + } + + @Override + protected void onSetState(int state) { + if (VERBOSE) log("onSetState, state: " + Connection.stateToString(state)); + } + + @Override + protected void onPlayDtmfTone(char c) { + if (VERBOSE) log("onPlayDtmfTone"); + } + + @Override + protected void onStopDtmfTone() { + if (VERBOSE) log("onStopDtmfTone"); + } + + @Override + protected void onDisconnect() { + if (VERBOSE) log("onDisconnect"); + } + + @Override + protected void onSeparate() { + if (VERBOSE) log("onSeparate"); + } + + @Override + protected void onAbort() { + if (VERBOSE) log("onAbort"); + } + + @Override + protected void onHold() { + if (VERBOSE) log("onHold"); + } + + @Override + protected void onUnhold() { + if (VERBOSE) log("onUnhold"); + } + + @Override + protected void onAnswer() { + if (VERBOSE) log("onAnswer"); + } + + @Override + protected void onReject() { + if (VERBOSE) log("onReject"); + } + + @Override + protected void onPostDialContinue(boolean proceed) { + if (VERBOSE) log("onPostDialContinue, proceed: " + proceed); + } + + @Override + protected void onChildrenChanged(List children) { + if (VERBOSE) log("onChildrenChanged, children: " + children); + } + + @Override + protected void onPhoneAccountClicked() { + if (VERBOSE) log("onPhoneAccountClicked"); + } + + 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 new file mode 100644 index 000000000..9b7b33fe2 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java @@ -0,0 +1,154 @@ +/* + * 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.net.sip.SipException; +import android.net.sip.SipManager; +import android.net.sip.SipProfile; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.Settings; +import android.telecomm.Connection; +import android.telecomm.ConnectionRequest; +import android.telecomm.ConnectionService; +import android.telecomm.Response; +import android.telephony.DisconnectCause; +import android.telephony.PhoneNumberUtils; +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 { + private static final String PREFIX = "[SipConnectionService] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + private class GetSipProfileTask extends AsyncTask { + 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 callback) { + if (VERBOSE) log("onCreateConnections, request: " + request); + new GetSipProfileTask(this, request, callback).execute(); + } + + @Override + protected void onCreateConferenceConnection( + String token, + Connection connection, + Response callback) { + if (VERBOSE) log("onCreateConferenceConnection, connection: " + connection); + } + + @Override + protected void onCreateIncomingConnection( + ConnectionRequest request, + Response callback) { + if (VERBOSE) log("onCreateIncomingConnection, request: " + request); + } + + @Override + protected void onConnectionAdded(Connection connection) { + if (VERBOSE) log("onConnectionAdded, connection: " + connection); + } + + @Override + protected void onConnectionRemoved(Connection connection) { + if (VERBOSE) log("onConnectionRemoved, connection: " + connection); + } + + private void onSipProfileChosen( + 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); + } + } else { + response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, + "Failed to find SIP profile"); + } + } + + protected void startCallWithPhone( + Phone phone, + ConnectionRequest request, + OutgoingCallResponse response) { + String number = request.getHandle().getSchemeSpecificPart(); + try { + com.android.internal.telephony.Connection connection = + phone.dial(number, request.getVideoState()); + SipConnection sipConnection = new SipConnection(connection); + response.onSuccess(request, sipConnection); + } 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); + } + } + + 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 new file mode 100644 index 000000000..a76529686 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipEditor.java @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2010 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 com.android.internal.telephony.CallManager; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.services.telephony.sip.SipUtil; + +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.Intent; +import android.net.sip.SipManager; +import android.net.sip.SipProfile; +import android.os.Bundle; +import android.os.Parcelable; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceGroup; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * The activity class for editing a new or existing SIP profile. + */ +public class SipEditor extends PreferenceActivity + implements Preference.OnPreferenceChangeListener { + private static final String PREFIX = "[SipEditor] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + private static final int MENU_SAVE = Menu.FIRST; + private static final int MENU_DISCARD = Menu.FIRST + 1; + private static final int MENU_REMOVE = Menu.FIRST + 2; + + private static final String KEY_PROFILE = "profile"; + private static final String GET_METHOD_PREFIX = "get"; + private static final char SCRAMBLED = '*'; + private static final int NA = 0; + + private PrimaryAccountSelector mPrimaryAccountSelector; + private AdvancedSettings mAdvancedSettings; + private SipSharedPreferences mSharedPreferences; + private boolean mDisplayNameSet; + private boolean mHomeButtonClicked; + private boolean mUpdateRequired; + private boolean mUpdatedFieldIsEmpty; + + private SipManager mSipManager; + private SipProfileDb mProfileDb; + private SipProfile mOldProfile; + private CallManager mCallManager; + private Button mRemoveButton; + + enum PreferenceKey { + Username(R.string.username, 0, R.string.default_preference_summary), + Password(R.string.password, 0, R.string.default_preference_summary), + DomainAddress(R.string.domain_address, 0, R.string.default_preference_summary), + DisplayName(R.string.display_name, 0, R.string.display_name_summary), + ProxyAddress(R.string.proxy_address, 0, R.string.optional_summary), + Port(R.string.port, R.string.default_port, R.string.default_port), + Transport(R.string.transport, R.string.default_transport, NA), + SendKeepAlive(R.string.send_keepalive, R.string.sip_system_decide, NA), + AuthUserName(R.string.auth_username, 0, R.string.optional_summary); + + final int text; + final int initValue; + final int defaultSummary; + Preference preference; + + /** + * @param key The key name of the preference. + * @param initValue The initial value of the preference. + * @param defaultSummary The default summary value of the preference + * when the preference value is empty. + */ + PreferenceKey(int text, int initValue, int defaultSummary) { + this.text = text; + this.initValue = initValue; + this.defaultSummary = defaultSummary; + } + + String getValue() { + if (preference instanceof EditTextPreference) { + return ((EditTextPreference) preference).getText(); + } else if (preference instanceof ListPreference) { + return ((ListPreference) preference).getValue(); + } + throw new RuntimeException("getValue() for the preference " + this); + } + + void setValue(String value) { + if (preference instanceof EditTextPreference) { + String oldValue = getValue(); + ((EditTextPreference) preference).setText(value); + if (this != Password) { + if (VERBOSE) { + log(this + ": setValue() " + value + ": " + oldValue + " --> " + + getValue()); + } + } + } else if (preference instanceof ListPreference) { + ((ListPreference) preference).setValue(value); + } + + if (TextUtils.isEmpty(value)) { + preference.setSummary(defaultSummary); + } else if (this == Password) { + preference.setSummary(scramble(value)); + } else if ((this == DisplayName) + && value.equals(getDefaultDisplayName())) { + preference.setSummary(defaultSummary); + } else { + preference.setSummary(value); + } + } + } + + @Override + public void onResume() { + super.onResume(); + mHomeButtonClicked = false; + if (mCallManager.getState() != PhoneConstants.State.IDLE) { + mAdvancedSettings.show(); + getPreferenceScreen().setEnabled(false); + if (mRemoveButton != null) mRemoveButton.setEnabled(false); + } else { + getPreferenceScreen().setEnabled(true); + if (mRemoveButton != null) mRemoveButton.setEnabled(true); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + if (VERBOSE) log("onCreate, start profile editor"); + super.onCreate(savedInstanceState); + + 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); + + SipProfile p = mOldProfile = (SipProfile) ((savedInstanceState == null) + ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE) + : savedInstanceState.getParcelable(KEY_PROFILE)); + + PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); + for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) { + setupPreference(screen.getPreference(i)); + } + + if (p == null) { + screen.setTitle(R.string.sip_edit_new_title); + } + + mAdvancedSettings = new AdvancedSettings(); + mPrimaryAccountSelector = new PrimaryAccountSelector(p); + + loadPreferencesFromProfile(p); + } + + @Override + public void onPause() { + if (VERBOSE) log("onPause, finishing: " + isFinishing()); + if (!isFinishing()) { + mHomeButtonClicked = true; + validateAndSetResult(); + } + super.onPause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(0, MENU_DISCARD, 0, R.string.sip_menu_discard) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(0, MENU_SAVE, 0, R.string.sip_menu_save) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(0, MENU_REMOVE, 0, R.string.remove_sip_account) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem removeMenu = menu.findItem(MENU_REMOVE); + removeMenu.setVisible(mOldProfile != null); + menu.findItem(MENU_SAVE).setEnabled(mUpdateRequired); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_SAVE: + validateAndSetResult(); + return true; + + case MENU_DISCARD: + finish(); + return true; + + case MENU_REMOVE: { + setRemovedProfileAndFinish(); + return true; + } + } + return super.onOptionsItemSelected(item); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + validateAndSetResult(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + private void saveAndRegisterProfile(SipProfile p) throws IOException { + if (p == null) return; + mProfileDb.saveProfile(p); + if (p.getAutoRegistration() || mSharedPreferences.isPrimaryAccount(p.getUriString())) { + try { + mSipManager.open(p, SipUtil.createIncomingCallPendingIntent(this), null); + } catch (Exception e) { + log("saveAndRegisterProfile, register failed for profile: " + p.getUriString() + + ", exception: " + e); + } + } + } + + private void deleteAndUnregisterProfile(SipProfile p) { + if (p == null) return; + mProfileDb.deleteProfile(p); + unregisterProfile(p.getUriString()); + } + + private void unregisterProfile(String uri) { + try { + mSipManager.close(uri); + } catch (Exception e) { + log("unregisterProfile, unregister failed for profile: " + uri + ", exception: " + e); + } + } + + private void setRemovedProfileAndFinish() { + Intent intent = new Intent(this, SipSettings.class); + setResult(RESULT_FIRST_USER, intent); + Toast.makeText(this, R.string.removing_account, Toast.LENGTH_SHORT) + .show(); + replaceProfile(mOldProfile, null); + // do finish() in replaceProfile() in a background thread + } + + private void showAlert(Throwable e) { + String msg = e.getMessage(); + if (TextUtils.isEmpty(msg)) msg = e.toString(); + showAlert(msg); + } + + private void showAlert(final String message) { + if (mHomeButtonClicked) { + if (VERBOSE) log("Home button clicked, don't show dialog: " + message); + return; + } + runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(SipEditor.this) + .setTitle(android.R.string.dialog_alert_title) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setMessage(message) + .setPositiveButton(R.string.alert_dialog_ok, null) + .show(); + } + }); + } + + private boolean isEditTextEmpty(PreferenceKey key) { + EditTextPreference pref = (EditTextPreference) key.preference; + return TextUtils.isEmpty(pref.getText()) + || pref.getSummary().equals(getString(key.defaultSummary)); + } + + private void validateAndSetResult() { + boolean allEmpty = true; + CharSequence firstEmptyFieldTitle = null; + for (PreferenceKey key : PreferenceKey.values()) { + Preference p = key.preference; + if (p instanceof EditTextPreference) { + EditTextPreference pref = (EditTextPreference) p; + boolean fieldEmpty = isEditTextEmpty(key); + if (allEmpty && !fieldEmpty) allEmpty = false; + + // use default value if display name is empty + if (fieldEmpty) { + switch (key) { + case DisplayName: + pref.setText(getDefaultDisplayName()); + break; + case AuthUserName: + case ProxyAddress: + // optional; do nothing + break; + case Port: + pref.setText(getString(R.string.default_port)); + break; + default: + if (firstEmptyFieldTitle == null) { + firstEmptyFieldTitle = pref.getTitle(); + } + } + } else if (key == PreferenceKey.Port) { + int port = Integer.parseInt(PreferenceKey.Port.getValue()); + if ((port < 1000) || (port > 65534)) { + showAlert(getString(R.string.not_a_valid_port)); + return; + } + } + } + } + + if (allEmpty || !mUpdateRequired) { + finish(); + return; + } else if (firstEmptyFieldTitle != null) { + showAlert(getString(R.string.empty_alert, firstEmptyFieldTitle)); + return; + } + try { + SipProfile profile = createSipProfile(); + Intent intent = new Intent(this, SipSettings.class); + intent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) profile); + setResult(RESULT_OK, intent); + Toast.makeText(this, R.string.saving_account, Toast.LENGTH_SHORT).show(); + + replaceProfile(mOldProfile, profile); + // do finish() in replaceProfile() in a background thread + } catch (Exception e) { + log("validateAndSetResult, can not create new SipProfile, exception: " + e); + showAlert(e); + } + } + + private void unregisterOldPrimaryAccount() { + String primaryAccountUri = mSharedPreferences.getPrimaryAccount(); + if (VERBOSE) log("unregisterOldPrimaryAccount, old primary: " + primaryAccountUri); + if ((primaryAccountUri != null) && !mSharedPreferences.isReceivingCallsEnabled()) { + if (VERBOSE) log("unregisterOldPrimaryAccount, calling unregister"); + unregisterProfile(primaryAccountUri); + } + } + + private void replaceProfile(final SipProfile oldProfile, final SipProfile newProfile) { + // Replace profile in a background thread as it takes time to access the + // storage; do finish() once everything goes fine. + // newProfile may be null if the old profile is to be deleted rather + // than being modified. + new Thread(new Runnable() { + public void run() { + try { + // if new profile is primary, unregister the old primary account + if ((newProfile != null) && mPrimaryAccountSelector.isSelected()) { + unregisterOldPrimaryAccount(); + } + + mPrimaryAccountSelector.commit(newProfile); + deleteAndUnregisterProfile(oldProfile); + saveAndRegisterProfile(newProfile); + finish(); + } catch (Exception e) { + log("replaceProfile, can not save/register new SipProfile, exception: " + e); + showAlert(e); + } + } + }, "SipEditor").start(); + } + + private String getProfileName() { + return PreferenceKey.Username.getValue() + "@" + + PreferenceKey.DomainAddress.getValue(); + } + + private SipProfile createSipProfile() throws Exception { + return new SipProfile.Builder( + PreferenceKey.Username.getValue(), + PreferenceKey.DomainAddress.getValue()) + .setProfileName(getProfileName()) + .setPassword(PreferenceKey.Password.getValue()) + .setOutboundProxy(PreferenceKey.ProxyAddress.getValue()) + .setProtocol(PreferenceKey.Transport.getValue()) + .setDisplayName(PreferenceKey.DisplayName.getValue()) + .setPort(Integer.parseInt(PreferenceKey.Port.getValue())) + .setSendKeepAlive(isAlwaysSendKeepAlive()) + .setAutoRegistration( + mSharedPreferences.isReceivingCallsEnabled()) + .setAuthUserName(PreferenceKey.AuthUserName.getValue()) + .build(); + } + + public boolean onPreferenceChange(Preference pref, Object newValue) { + if (!mUpdateRequired) { + mUpdateRequired = true; + if (mOldProfile != null) { + unregisterProfile(mOldProfile.getUriString()); + } + } + if (pref instanceof CheckBoxPreference) { + invalidateOptionsMenu(); + return true; + } + String value = (newValue == null) ? "" : newValue.toString(); + if (TextUtils.isEmpty(value)) { + pref.setSummary(getPreferenceKey(pref).defaultSummary); + } else if (pref == PreferenceKey.Password.preference) { + pref.setSummary(scramble(value)); + } else { + pref.setSummary(value); + } + + if (pref == PreferenceKey.DisplayName.preference) { + ((EditTextPreference) pref).setText(value); + checkIfDisplayNameSet(); + } + + // SAVE menu should be enabled once the user modified some preference. + invalidateOptionsMenu(); + return true; + } + + private PreferenceKey getPreferenceKey(Preference pref) { + for (PreferenceKey key : PreferenceKey.values()) { + if (key.preference == pref) return key; + } + throw new RuntimeException("not possible to reach here"); + } + + private void loadPreferencesFromProfile(SipProfile p) { + if (p != null) { + if (VERBOSE) log("loadPreferencesFromProfile, existing profile: " + p.getProfileName()); + try { + Class profileClass = SipProfile.class; + for (PreferenceKey key : PreferenceKey.values()) { + Method meth = profileClass.getMethod(GET_METHOD_PREFIX + + getString(key.text), (Class[])null); + if (key == PreferenceKey.SendKeepAlive) { + boolean value = ((Boolean) meth.invoke(p, (Object[]) null)).booleanValue(); + key.setValue(getString(value + ? R.string.sip_always_send_keepalive + : R.string.sip_system_decide)); + } else { + Object value = meth.invoke(p, (Object[])null); + key.setValue((value == null) ? "" : value.toString()); + } + } + checkIfDisplayNameSet(); + } catch (Exception e) { + log("loadPreferencesFromProfile, can not load pref from profile, exception: " + e); + } + } else { + if (VERBOSE) log("loadPreferencesFromProfile, edit a new profile"); + for (PreferenceKey key : PreferenceKey.values()) { + key.preference.setOnPreferenceChangeListener(this); + + // FIXME: android:defaultValue in preference xml file doesn't + // work. Even if we setValue() for each preference in the case + // of (p != null), the dialog still shows android:defaultValue, + // not the value set by setValue(). This happens if + // android:defaultValue is not empty. Is it a bug? + if (key.initValue != 0) { + key.setValue(getString(key.initValue)); + } + } + mDisplayNameSet = false; + } + } + + private boolean isAlwaysSendKeepAlive() { + ListPreference pref = (ListPreference) PreferenceKey.SendKeepAlive.preference; + return getString(R.string.sip_always_send_keepalive).equals(pref.getValue()); + } + + private void setCheckBox(PreferenceKey key, boolean checked) { + CheckBoxPreference pref = (CheckBoxPreference) key.preference; + pref.setChecked(checked); + } + + private void setupPreference(Preference pref) { + pref.setOnPreferenceChangeListener(this); + for (PreferenceKey key : PreferenceKey.values()) { + String name = getString(key.text); + if (name.equals(pref.getKey())) { + key.preference = pref; + return; + } + } + } + + private void checkIfDisplayNameSet() { + String displayName = PreferenceKey.DisplayName.getValue(); + mDisplayNameSet = !TextUtils.isEmpty(displayName) + && !displayName.equals(getDefaultDisplayName()); + if (VERBOSE) log("checkIfDisplayNameSet, displayName set: " + mDisplayNameSet); + if (mDisplayNameSet) { + PreferenceKey.DisplayName.preference.setSummary(displayName); + } else { + PreferenceKey.DisplayName.setValue(""); + } + } + + private static String getDefaultDisplayName() { + return PreferenceKey.Username.getValue(); + } + + private static String scramble(String s) { + char[] cc = new char[s.length()]; + Arrays.fill(cc, SCRAMBLED); + return new String(cc); + } + + // only takes care of the primary account setting in SipSharedSettings + private class PrimaryAccountSelector { + private CheckBoxPreference mCheckbox; + private final boolean mWasPrimaryAccount; + + // @param profile profile to be edited; null if adding new profile + PrimaryAccountSelector(SipProfile profile) { + mCheckbox = (CheckBoxPreference) getPreferenceScreen() + .findPreference(getString(R.string.set_primary)); + boolean noPrimaryAccountSet = !mSharedPreferences.hasPrimaryAccount(); + boolean editNewProfile = (profile == null); + mWasPrimaryAccount = !editNewProfile && mSharedPreferences.isPrimaryAccount( + profile.getUriString()); + + if (VERBOSE) { + log(" noPrimaryAccountSet: " + noPrimaryAccountSet); + log(" editNewProfile: " + editNewProfile); + log(" mWasPrimaryAccount: " + mWasPrimaryAccount); + } + + mCheckbox.setChecked(mWasPrimaryAccount || (editNewProfile && noPrimaryAccountSet)); + } + + boolean isSelected() { + return mCheckbox.isChecked(); + } + + // profile is null if the user removes it + void commit(SipProfile profile) { + if ((profile != null) && mCheckbox.isChecked()) { + mSharedPreferences.setPrimaryAccount(profile.getUriString()); + } else if (mWasPrimaryAccount) { + mSharedPreferences.unsetPrimaryAccount(); + } + if (VERBOSE) { + log("PrimaryAccountSelector.commit, new primary account: " + + mSharedPreferences.getPrimaryAccount()); + } + } + } + + private class AdvancedSettings implements Preference.OnPreferenceClickListener { + private Preference mAdvancedSettingsTrigger; + private Preference[] mPreferences; + private boolean mShowing = false; + + AdvancedSettings() { + mAdvancedSettingsTrigger = getPreferenceScreen().findPreference( + getString(R.string.advanced_settings)); + mAdvancedSettingsTrigger.setOnPreferenceClickListener(this); + + loadAdvancedPreferences(); + } + + private void loadAdvancedPreferences() { + PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); + + addPreferencesFromResource(R.xml.sip_advanced_edit); + PreferenceGroup group = (PreferenceGroup) screen.findPreference( + getString(R.string.advanced_settings_container)); + screen.removePreference(group); + + mPreferences = new Preference[group.getPreferenceCount()]; + int order = screen.getPreferenceCount(); + for (int i = 0, n = mPreferences.length; i < n; i++) { + Preference pref = group.getPreference(i); + pref.setOrder(order++); + setupPreference(pref); + mPreferences[i] = pref; + } + } + + void show() { + mShowing = true; + mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_hide); + PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); + for (Preference pref : mPreferences) { + screen.addPreference(pref); + if (VERBOSE) { + log("AdvancedSettings.show, pref: " + pref.getKey() + ", order: " + + pref.getOrder()); + } + } + } + + private void hide() { + mShowing = false; + mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_show); + PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); + for (Preference pref : mPreferences) { + screen.removePreference(pref); + } + } + + public boolean onPreferenceClick(Preference preference) { + if (VERBOSE) log("AdvancedSettings.onPreferenceClick"); + if (!mShowing) { + show(); + } else { + hide(); + } + return true; + } + } + + 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 new file mode 100644 index 000000000..bf4b6bbe0 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipProfileDb.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 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 com.android.internal.os.AtomicFile; + +import android.content.Context; +import android.net.sip.SipProfile; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Utility class that helps perform operations on the SipProfile database. + */ +public class SipProfileDb { + private static final String PREFIX = "[SipProfileDb] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + private static final String PROFILES_DIR = "/profiles/"; + private static final String PROFILE_OBJ_FILE = ".pobj"; + + private String mProfilesDirectory; + private SipSharedPreferences mSipSharedPreferences; + private int mProfilesCount = -1; + + public SipProfileDb(Context context) { + mProfilesDirectory = context.getFilesDir().getAbsolutePath() + PROFILES_DIR; + mSipSharedPreferences = new SipSharedPreferences(context); + } + + public void deleteProfile(SipProfile p) { + synchronized(SipProfileDb.class) { + deleteProfile(new File(mProfilesDirectory + p.getProfileName())); + if (mProfilesCount < 0) retrieveSipProfileListInternal(); + mSipSharedPreferences.setProfilesCount(--mProfilesCount); + } + } + + private void deleteProfile(File file) { + if (file.isDirectory()) { + for (File child : file.listFiles()) deleteProfile(child); + } + file.delete(); + } + + public void saveProfile(SipProfile p) throws IOException { + synchronized(SipProfileDb.class) { + if (mProfilesCount < 0) retrieveSipProfileListInternal(); + File f = new File(mProfilesDirectory + p.getProfileName()); + if (!f.exists()) f.mkdirs(); + AtomicFile atomicFile = new AtomicFile(new File(f, PROFILE_OBJ_FILE)); + FileOutputStream fos = null; + ObjectOutputStream oos = null; + try { + fos = atomicFile.startWrite(); + oos = new ObjectOutputStream(fos); + oos.writeObject(p); + oos.flush(); + mSipSharedPreferences.setProfilesCount(++mProfilesCount); + atomicFile.finishWrite(fos); + } catch (IOException e) { + atomicFile.failWrite(fos); + throw e; + } finally { + if (oos != null) oos.close(); + } + } + } + + public int getProfilesCount() { + return (mProfilesCount < 0) ? mSipSharedPreferences.getProfilesCount() : mProfilesCount; + } + + public List retrieveSipProfileList() { + synchronized(SipProfileDb.class) { + return retrieveSipProfileListInternal(); + } + } + + private List retrieveSipProfileListInternal() { + List sipProfileList = Collections.synchronizedList( + new ArrayList()); + + File root = new File(mProfilesDirectory); + String[] dirs = root.list(); + if (dirs == null) return sipProfileList; + for (String dir : dirs) { + File f = new File(new File(root, dir), PROFILE_OBJ_FILE); + if (!f.exists()) continue; + try { + SipProfile p = deserialize(f); + if (p == null) continue; + if (!dir.equals(p.getProfileName())) continue; + + sipProfileList.add(p); + } catch (IOException e) { + log("retrieveSipProfileListInternal, exception: " + e); + } + } + mProfilesCount = sipProfileList.size(); + mSipSharedPreferences.setProfilesCount(mProfilesCount); + return sipProfileList; + } + + private SipProfile deserialize(File profileObjectFile) throws IOException { + AtomicFile atomicFile = new AtomicFile(profileObjectFile); + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(atomicFile.openRead()); + SipProfile p = (SipProfile) ois.readObject(); + return p; + } catch (ClassNotFoundException e) { + log("deserialize, exception: " + e); + } finally { + if (ois!= null) ois.close(); + } + return null; + } + + private static void log(String msg) { + Log.d(SipUtil.LOG_TAG, PREFIX + msg); + } +} diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java new file mode 100644 index 000000000..cf35e31fa --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipSettings.java @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2010 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 com.android.internal.telephony.CallManager; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; + +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.net.sip.SipErrorCode; +import android.net.sip.SipException; +import android.net.sip.SipManager; +import android.net.sip.SipProfile; +import android.net.sip.SipRegistrationListener; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.Process; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * The PreferenceActivity class for managing sip profile preferences. + */ +public class SipSettings extends PreferenceActivity { + private static final String PREFIX = "[SipSettings] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; + + private static final int MENU_ADD_ACCOUNT = Menu.FIRST; + + static final String KEY_SIP_PROFILE = "sip_profile"; + + private static final String BUTTON_SIP_RECEIVE_CALLS = + "sip_receive_calls_key"; + private static final String PREF_SIP_LIST = "sip_account_list"; + + private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1; + + private PackageManager mPackageManager; + private SipManager mSipManager; + private CallManager mCallManager; + private SipProfileDb mProfileDb; + + private SipProfile mProfile; // profile that's being edited + + private CheckBoxPreference mButtonSipReceiveCalls; + private PreferenceCategory mSipListContainer; + private Map mSipPreferenceMap; + private List mSipProfileList; + private SipSharedPreferences mSipSharedPreferences; + private int mUid = Process.myUid(); + + private class SipPreference extends Preference { + SipProfile mProfile; + SipPreference(Context c, SipProfile p) { + super(c); + setProfile(p); + } + + SipProfile getProfile() { + return mProfile; + } + + void setProfile(SipProfile p) { + mProfile = p; + setTitle(getProfileName(p)); + updateSummary(mSipSharedPreferences.isReceivingCallsEnabled() + ? getString(R.string.registration_status_checking_status) + : getString(R.string.registration_status_not_receiving)); + } + + void updateSummary(String registrationStatus) { + int profileUid = mProfile.getCallingUid(); + boolean isPrimary = mProfile.getUriString().equals( + mSipSharedPreferences.getPrimaryAccount()); + if (VERBOSE) { + log("SipPreference.updateSummary, profile uid: " + profileUid + + " isPrimary: " + isPrimary + + " registration: " + registrationStatus + + " Primary: " + mSipSharedPreferences.getPrimaryAccount() + + " status: " + registrationStatus); + } + String summary = ""; + if ((profileUid > 0) && (profileUid != mUid)) { + // from third party apps + summary = getString(R.string.third_party_account_summary, + getPackageNameFromUid(profileUid)); + } else if (isPrimary) { + summary = getString(R.string.primary_account_summary_with, + registrationStatus); + } else { + summary = registrationStatus; + } + setSummary(summary); + } + } + + private String getPackageNameFromUid(int uid) { + try { + String[] pkgs = mPackageManager.getPackagesForUid(uid); + ApplicationInfo ai = mPackageManager.getApplicationInfo(pkgs[0], 0); + return ai.loadLabel(mPackageManager).toString(); + } catch (PackageManager.NameNotFoundException e) { + log("getPackageNameFromUid, cannot find name of uid: " + uid + ", exception: " + e); + } + return "uid:" + uid; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mSipManager = SipManager.newInstance(this); + mSipSharedPreferences = new SipSharedPreferences(this); + mProfileDb = new SipProfileDb(this); + + mPackageManager = getPackageManager(); + setContentView(R.layout.sip_settings_ui); + addPreferencesFromResource(R.xml.sip_setting); + mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST); + registerForReceiveCallsCheckBox(); + mCallManager = CallManager.getInstance(); + + updateProfilesStatus(); + + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + + @Override + public void onResume() { + super.onResume(); + + if (mCallManager.getState() != PhoneConstants.State.IDLE) { + mButtonSipReceiveCalls.setEnabled(false); + } else { + mButtonSipReceiveCalls.setEnabled(true); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterForContextMenu(getListView()); + } + + @Override + protected void onActivityResult(final int requestCode, final int resultCode, + final Intent intent) { + if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return; + new Thread() { + @Override + public void run() { + try { + if (mProfile != null) { + if (VERBOSE) log("onActivityResult, remove: " + mProfile.getProfileName()); + deleteProfile(mProfile); + } + + SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE); + if (resultCode == RESULT_OK) { + if (VERBOSE) log("onActivityResult, new: " + profile.getProfileName()); + addProfile(profile); + } + updateProfilesStatus(); + } catch (IOException e) { + log("onActivityResult, can not handle the profile: " + e); + } + } + }.start(); + } + + private void registerForReceiveCallsCheckBox() { + mButtonSipReceiveCalls = (CheckBoxPreference) findPreference + (BUTTON_SIP_RECEIVE_CALLS); + mButtonSipReceiveCalls.setChecked( + mSipSharedPreferences.isReceivingCallsEnabled()); + mButtonSipReceiveCalls.setOnPreferenceClickListener( + new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + final boolean enabled = ((CheckBoxPreference) preference).isChecked(); + new Thread(new Runnable() { + public void run() { + handleSipReceiveCallsOption(enabled); + } + }).start(); + return true; + } + }); + } + + private synchronized void handleSipReceiveCallsOption(boolean enabled) { + mSipSharedPreferences.setReceivingCallsEnabled(enabled); + List sipProfileList = mProfileDb.retrieveSipProfileList(); + for (SipProfile p : sipProfileList) { + String sipUri = p.getUriString(); + p = updateAutoRegistrationFlag(p, enabled); + try { + if (enabled) { + mSipManager.open(p, SipUtil.createIncomingCallPendingIntent(this), null); + } else { + mSipManager.close(sipUri); + if (mSipSharedPreferences.isPrimaryAccount(sipUri)) { + // re-open in order to make calls + mSipManager.open(p); + } + } + } catch (Exception e) { + log("handleSipReceiveCallsOption, register failed: " + e); + } + } + updateProfilesStatus(); + } + + private SipProfile updateAutoRegistrationFlag( + SipProfile p, boolean enabled) { + SipProfile newProfile = new SipProfile.Builder(p) + .setAutoRegistration(enabled) + .build(); + try { + mProfileDb.deleteProfile(p); + mProfileDb.saveProfile(newProfile); + } catch (Exception e) { + log("updateAutoRegistrationFlag, exception: " + e); + } + return newProfile; + } + + private void updateProfilesStatus() { + new Thread(new Runnable() { + @Override + public void run() { + try { + retrieveSipLists(); + } catch (Exception e) { + log("updateProfilesStatus, exception: " + e); + } + } + }).start(); + } + + private String getProfileName(SipProfile profile) { + String profileName = profile.getProfileName(); + if (TextUtils.isEmpty(profileName)) { + profileName = profile.getUserName() + "@" + profile.getSipDomain(); + } + return profileName; + } + + private void retrieveSipLists() { + mSipPreferenceMap = new LinkedHashMap(); + mSipProfileList = mProfileDb.retrieveSipProfileList(); + processActiveProfilesFromSipService(); + Collections.sort(mSipProfileList, new Comparator() { + @Override + public int compare(SipProfile p1, SipProfile p2) { + return getProfileName(p1).compareTo(getProfileName(p2)); + } + + public boolean equals(SipProfile p) { + // not used + return false; + } + }); + mSipListContainer.removeAll(); + for (SipProfile p : mSipProfileList) { + addPreferenceFor(p); + } + + if (!mSipSharedPreferences.isReceivingCallsEnabled()) return; + for (SipProfile p : mSipProfileList) { + if (mUid == p.getCallingUid()) { + try { + mSipManager.setRegistrationListener( + p.getUriString(), createRegistrationListener()); + } catch (SipException e) { + log("retrieveSipLists, cannot set registration listener: " + e); + } + } + } + } + + private void processActiveProfilesFromSipService() { + SipProfile[] activeList = mSipManager.getListOfProfiles(); + for (SipProfile activeProfile : activeList) { + SipProfile profile = getProfileFromList(activeProfile); + if (profile == null) { + mSipProfileList.add(activeProfile); + } else { + profile.setCallingUid(activeProfile.getCallingUid()); + } + } + } + + private SipProfile getProfileFromList(SipProfile activeProfile) { + for (SipProfile p : mSipProfileList) { + if (p.getUriString().equals(activeProfile.getUriString())) { + return p; + } + } + return null; + } + + private void addPreferenceFor(SipProfile p) { + String status; + if (VERBOSE) log("addPreferenceFor, profile uri: " + p.getUri()); + SipPreference pref = new SipPreference(this, p); + mSipPreferenceMap.put(p.getUriString(), pref); + mSipListContainer.addPreference(pref); + + pref.setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference pref) { + handleProfileClick(((SipPreference) pref).mProfile); + return true; + } + }); + } + + private void handleProfileClick(final SipProfile profile) { + int uid = profile.getCallingUid(); + if (uid == mUid || uid == 0) { + startSipEditor(profile); + return; + } + new AlertDialog.Builder(this) + .setTitle(R.string.alert_dialog_close) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setPositiveButton(R.string.close_profile, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int w) { + deleteProfile(profile); + unregisterProfile(profile); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + private void unregisterProfile(final SipProfile p) { + // run it on background thread for better UI response + new Thread(new Runnable() { + @Override + public void run() { + try { + mSipManager.close(p.getUriString()); + } catch (Exception e) { + log("unregisterProfile, unregister failed, SipService died? Exception: " + e); + } + } + }, "unregisterProfile").start(); + } + + void deleteProfile(SipProfile p) { + mSipProfileList.remove(p); + SipPreference pref = mSipPreferenceMap.remove(p.getUriString()); + mSipListContainer.removePreference(pref); + } + + private void addProfile(SipProfile p) throws IOException { + try { + mSipManager.setRegistrationListener(p.getUriString(), + createRegistrationListener()); + } catch (Exception e) { + log("addProfile, cannot set registration listener: " + e); + } + mSipProfileList.add(p); + addPreferenceFor(p); + } + + private void startSipEditor(final SipProfile profile) { + mProfile = profile; + Intent intent = new Intent(this, SipEditor.class); + intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile); + startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE); + } + + private void showRegistrationMessage(final String profileUri, + final String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + SipPreference pref = mSipPreferenceMap.get(profileUri); + if (pref != null) { + pref.updateSummary(message); + } + } + }); + } + + private SipRegistrationListener createRegistrationListener() { + return new SipRegistrationListener() { + @Override + public void onRegistrationDone(String profileUri, long expiryTime) { + showRegistrationMessage(profileUri, getString( + R.string.registration_status_done)); + } + + @Override + public void onRegistering(String profileUri) { + showRegistrationMessage(profileUri, getString( + R.string.registration_status_registering)); + } + + @Override + public void onRegistrationFailed(String profileUri, int errorCode, + String message) { + switch (errorCode) { + case SipErrorCode.IN_PROGRESS: + showRegistrationMessage(profileUri, getString( + R.string.registration_status_still_trying)); + break; + case SipErrorCode.INVALID_CREDENTIALS: + showRegistrationMessage(profileUri, getString( + R.string.registration_status_invalid_credentials)); + break; + case SipErrorCode.SERVER_UNREACHABLE: + showRegistrationMessage(profileUri, getString( + R.string.registration_status_server_unreachable)); + break; + case SipErrorCode.DATA_CONNECTION_LOST: + if (SipManager.isSipWifiOnly(getApplicationContext())){ + showRegistrationMessage(profileUri, getString( + R.string.registration_status_no_wifi_data)); + } else { + showRegistrationMessage(profileUri, getString( + R.string.registration_status_no_data)); + } + break; + case SipErrorCode.CLIENT_ERROR: + showRegistrationMessage(profileUri, getString( + R.string.registration_status_not_running)); + break; + default: + showRegistrationMessage(profileUri, getString( + R.string.registration_status_failed_try_later, + message)); + } + } + }; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_sip_account) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(MENU_ADD_ACCOUNT).setEnabled( + mCallManager.getState() == PhoneConstants.State.IDLE); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int itemId = item.getItemId(); + switch (itemId) { + case MENU_ADD_ACCOUNT: { + startSipEditor(null); + return true; + } + } + return super.onOptionsItemSelected(item); + } + + private static void log(String msg) { + Log.d(SipUtil.LOG_TAG, PREFIX + msg); + } +} diff --git a/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java b/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java new file mode 100644 index 000000000..ffb1513d2 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 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.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.Log; + +/** + * Wrapper for SIP's shared preferences. + */ +public class SipSharedPreferences { + private static final String PREFIX = "[SipSharedPreferences] "; + private static final boolean VERBOSE = true; /* STOP SHIP if true */ + + private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; + private static final String KEY_PRIMARY_ACCOUNT = "primary"; + private static final String KEY_NUMBER_OF_PROFILES = "profiles"; + + private SharedPreferences mPreferences; + private Context mContext; + + public SipSharedPreferences(Context context) { + mPreferences = context.getSharedPreferences( + SIP_SHARED_PREFERENCES, Context.MODE_WORLD_READABLE); + mContext = context; + } + + public void setPrimaryAccount(String accountUri) { + SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(KEY_PRIMARY_ACCOUNT, accountUri); + editor.apply(); + } + + public void unsetPrimaryAccount() { + setPrimaryAccount(null); + } + + /** Returns the primary account URI or null if it does not exist. */ + public String getPrimaryAccount() { + return mPreferences.getString(KEY_PRIMARY_ACCOUNT, null); + } + + public boolean isPrimaryAccount(String accountUri) { + return accountUri.equals( + mPreferences.getString(KEY_PRIMARY_ACCOUNT, null)); + } + + public boolean hasPrimaryAccount() { + return !TextUtils.isEmpty( + mPreferences.getString(KEY_PRIMARY_ACCOUNT, null)); + } + + public void setProfilesCount(int number) { + SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(KEY_NUMBER_OF_PROFILES, number); + editor.apply(); + } + + public int getProfilesCount() { + return mPreferences.getInt(KEY_NUMBER_OF_PROFILES, 0); + } + + public void setSipCallOption(String option) { + Settings.System.putString(mContext.getContentResolver(), + Settings.System.SIP_CALL_OPTIONS, option); + } + + public String getSipCallOption() { + String option = Settings.System.getString(mContext.getContentResolver(), + Settings.System.SIP_CALL_OPTIONS); + return (option != null) ? option + : mContext.getString(R.string.sip_address_only); + } + + public void setReceivingCallsEnabled(boolean enabled) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SIP_RECEIVE_CALLS, (enabled ? 1 : 0)); + } + + public boolean isReceivingCallsEnabled() { + try { + return (Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SIP_RECEIVE_CALLS) != 0); + } catch (SettingNotFoundException e) { + log("isReceivingCallsEnabled, option not set; use default value, exception: " + e); + return false; + } + } + + // TODO: back up to Android Backup + + private static void log(String msg) { + Log.d(SipUtil.LOG_TAG, PREFIX + msg); + } +} diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java new file mode 100644 index 000000000..39b565523 --- /dev/null +++ b/sip/src/com/android/services/telephony/sip/SipUtil.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.sip.SipManager; + +public class SipUtil { + public static final String LOG_TAG = "SIP"; + + private static boolean sIsVoipSupported; + private static boolean sIsVoipSupportedInitialized; + + private SipUtil() { + } + + public static boolean isVoipSupported(Context context) { + if (!sIsVoipSupportedInitialized) { + sIsVoipSupported = SipManager.isVoipSupported(context) && + context.getResources().getBoolean( + com.android.internal.R.bool.config_built_in_sip_phone) && + context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); + } + return sIsVoipSupported; + } + + public 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); + } +} diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java index 289767d3f..377ca95fd 100644 --- a/src/com/android/phone/CallFeaturesSetting.java +++ b/src/com/android/phone/CallFeaturesSetting.java @@ -68,7 +68,8 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.cdma.TtyIntent; -import com.android.phone.sip.SipSharedPreferences; +import com.android.services.telephony.sip.SipSharedPreferences; +import com.android.services.telephony.sip.SipUtil; import java.util.Collection; import java.util.HashMap; @@ -1751,10 +1752,11 @@ public class CallFeaturesSetting extends PreferenceActivity private void createSipCallSettings() { // Add Internet call settings. - if (PhoneUtils.isVoipSupported()) { + if (SipUtil.isVoipSupported(this)) { mSipManager = SipManager.newInstance(this); mSipSharedPreferences = new SipSharedPreferences(this); - addPreferencesFromResource(R.xml.sip_settings_category); + addPreferencesFromResource( + com.android.services.telephony.sip.R.xml.sip_settings_category); mButtonSipCallOptions = getSipCallOptionPreference(); mButtonSipCallOptions.setOnPreferenceChangeListener(this); mButtonSipCallOptions.setValueIndex( diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java index 636a259d0..fc5cdab9c 100644 --- a/src/com/android/phone/OutgoingCallBroadcaster.java +++ b/src/com/android/phone/OutgoingCallBroadcaster.java @@ -309,35 +309,7 @@ public class OutgoingCallBroadcaster extends Activity */ private void startSipCallOptionHandler(Context context, Intent intent, Uri uri, String number) { - if (VDBG) { - Log.i(TAG, "startSipCallOptionHandler..."); - Log.i(TAG, "- intent: " + intent); - Log.i(TAG, "- uri: " + uri); - Log.i(TAG, "- number: " + number); - } - - // Create a copy of the original CALL intent that started the whole - // outgoing-call sequence. This intent will ultimately be passed to - // CallController.placeCall() after the SipCallOptionHandler step. - - Intent newIntent = new Intent(Intent.ACTION_CALL, uri); - newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number); - CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, newIntent); - - // Finally, launch the SipCallOptionHandler, with the copy of the - // original CALL intent stashed away in the EXTRA_NEW_CALL_INTENT - // extra. - - Intent selectPhoneIntent = new Intent(ACTION_SIP_SELECT_PHONE, uri); - selectPhoneIntent.setClass(context, SipCallOptionHandler.class); - selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT, newIntent); - selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (DBG) { - Log.v(TAG, "startSipCallOptionHandler(): " + - "calling startActivity: " + selectPhoneIntent); - } - context.startActivity(selectPhoneIntent); - // ...and see SipCallOptionHandler.onCreate() for the next step of the sequence. + // TODO(sail): Remove this code. } /** diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java index 9452fc5f6..60950a5a5 100644 --- a/src/com/android/phone/PhoneUtils.java +++ b/src/com/android/phone/PhoneUtils.java @@ -31,7 +31,6 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.Uri; -import android.net.sip.SipManager; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; @@ -2647,21 +2646,6 @@ public class PhoneUtils { return (state == Call.State.INCOMING && !PhoneGlobals.getInstance().mCM.hasActiveFgCall()); } - private static boolean sVoipSupported = false; - static { - PhoneGlobals app = PhoneGlobals.getInstance(); - sVoipSupported = SipManager.isVoipSupported(app) - && app.getResources().getBoolean(com.android.internal.R.bool.config_built_in_sip_phone) - && app.getResources().getBoolean(com.android.internal.R.bool.config_voice_capable); - } - - /** - * @return true if this device supports voice calls using the built-in SIP stack. - */ - public static boolean isVoipSupported() { - return sVoipSupported; - } - public static String getPresentationString(Context context, int presentation) { String name = context.getString(R.string.unknown); if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { diff --git a/src/com/android/phone/SipBroadcastReceiver.java b/src/com/android/phone/SipBroadcastReceiver.java deleted file mode 100644 index 8fdc7f730..000000000 --- a/src/com/android/phone/SipBroadcastReceiver.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2010 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.phone; - -import com.android.internal.telephony.CallManager; -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 com.android.phone.sip.SipProfileDb; -import com.android.phone.sip.SipSharedPreferences; - -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.telephony.Rlog; -import java.util.List; - -/** - * Broadcast receiver that handles SIP-related intents. - */ -public class SipBroadcastReceiver extends BroadcastReceiver { - private static final String TAG = SipBroadcastReceiver.class.getSimpleName(); - private static final boolean DBG = true; - private SipSharedPreferences mSipSharedPreferences; - - @Override - public void onReceive(Context context, final Intent intent) { - String action = intent.getAction(); - - if (!PhoneUtils.isVoipSupported()) { - if (DBG) log("SIP VOIP not supported: " + action); - return; - } - mSipSharedPreferences = new SipSharedPreferences(context); - - if (action.equals(SipManager.ACTION_SIP_INCOMING_CALL)) { - takeCall(intent); - } else if (action.equals(SipManager.ACTION_SIP_ADD_PHONE)) { - String localSipUri = intent.getStringExtra(SipManager.EXTRA_LOCAL_URI); - SipPhone phone = PhoneFactory.makeSipPhone(localSipUri); - if (phone != null) { - CallManager.getInstance().registerPhone(phone); - } - if (DBG) log("onReceive: add phone" + localSipUri + " #phones=" - + CallManager.getInstance().getAllPhones().size()); - } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PHONE)) { - String localSipUri = intent.getStringExtra(SipManager.EXTRA_LOCAL_URI); - removeSipPhone(localSipUri); - if (DBG) log("onReceive: remove phone: " + localSipUri + " #phones=" - + CallManager.getInstance().getAllPhones().size()); - } else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP)) { - if (DBG) log("onReceive: start auto registration"); - registerAllProfiles(); - } else { - if (DBG) log("onReceive: action not processed: " + action); - return; - } - } - - private void removeSipPhone(String sipUri) { - for (Phone phone : CallManager.getInstance().getAllPhones()) { - if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) { - if (((SipPhone) phone).getSipUri().equals(sipUri)) { - CallManager.getInstance().unregisterPhone(phone); - return; - } - } - } - if (DBG) log("RemoveSipPhone: failed:cannot find phone with uri " + sipUri); - } - - private void takeCall(Intent intent) { - Context phoneContext = PhoneGlobals.getInstance(); - try { - SipAudioCall sipAudioCall = SipManager.newInstance(phoneContext) - .takeAudioCall(intent, null); - for (Phone phone : CallManager.getInstance().getAllPhones()) { - if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) { - if (((SipPhone) phone).canTake(sipAudioCall)) { - if (DBG) log("takeCall: SIP call: " + intent); - return; - } - } - } - if (DBG) log("takeCall: not taken, drop SIP call: " + intent); - } catch (SipException e) { - loge("takeCall: error incoming SIP call", e); - } - } - - private void registerAllProfiles() { - final Context context = PhoneGlobals.getInstance(); - new Thread(new Runnable() { - @Override - public void run() { - SipManager sipManager = SipManager.newInstance(context); - SipProfileDb profileDb = new SipProfileDb(context); - List sipProfileList = - profileDb.retrieveSipProfileList(); - for (SipProfile profile : sipProfileList) { - try { - if (!profile.getAutoRegistration() && - !profile.getUriString().equals( - mSipSharedPreferences.getPrimaryAccount())) { - continue; - } - sipManager.open(profile, - SipUtil.createIncomingCallPendingIntent(), - null); - if (DBG) log("registerAllProfiles: profile=" + profile); - } catch (SipException e) { - loge("registerAllProfiles: failed" + profile.getProfileName(), e); - } - } - }} - ).start(); - } - - private void log(String s) { - Rlog.d(TAG, s); - } - - private void loge(String s, Throwable t) { - Rlog.e(TAG, s, t); - } -} diff --git a/src/com/android/phone/SipCallOptionHandler.java b/src/com/android/phone/SipCallOptionHandler.java deleted file mode 100644 index 47c5702d3..000000000 --- a/src/com/android/phone/SipCallOptionHandler.java +++ /dev/null @@ -1,499 +0,0 @@ -/** - * Copyright (C) 2010 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.phone; - -import com.android.internal.telephony.CallManager; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneFactory; -import com.android.phone.sip.SipProfileDb; -import com.android.phone.sip.SipSettings; -import com.android.phone.sip.SipSharedPreferences; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.net.sip.SipException; -import android.net.sip.SipManager; -import android.net.sip.SipProfile; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.SystemProperties; -import android.provider.Settings; -import android.telephony.PhoneNumberUtils; -import android.telephony.TelephonyManager; -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; - -/** - * Activity that selects the proper phone type for an outgoing call. - * - * This activity determines which Phone type (SIP or PSTN) should be used - * for an outgoing phone call, depending on the outgoing "number" (which - * may be either a PSTN number or a SIP address) as well as the user's SIP - * preferences. In some cases this activity has no interaction with the - * user, but in other cases it may (by bringing up a dialog if the user's - * preference is "Ask for each call".) - */ -public class SipCallOptionHandler extends Activity implements - DialogInterface.OnClickListener, DialogInterface.OnCancelListener, - CompoundButton.OnCheckedChangeListener { - static final String TAG = "SipCallOptionHandler"; - private static final boolean DBG = - (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); - - static final int DIALOG_SELECT_PHONE_TYPE = 0; - static final int DIALOG_SELECT_OUTGOING_SIP_PHONE = 1; - static final int DIALOG_START_SIP_SETTINGS = 2; - static final int DIALOG_NO_INTERNET_ERROR = 3; - static final int DIALOG_NO_VOIP = 4; - static final int DIALOG_SELECT_WIFI_CALL = 5; - static final int DIALOG_SIZE = 6; - - private Intent mIntent; - private List mProfileList; - private String mCallOption; - private String mNumber; - private SipSharedPreferences mSipSharedPreferences; - private SipProfileDb mSipProfileDb; - private Dialog[] mDialogs = new Dialog[DIALOG_SIZE]; - private SipProfile mOutgoingSipProfile; - private TextView mUnsetPriamryHint; - private boolean mUseSipPhone = false; - private boolean mMakePrimary = false; - private ComponentName mThirdPartyCallComponent; - - private static final int EVENT_DELAYED_FINISH = 1; - - private static final int DELAYED_FINISH_TIME = 2000; // msec - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (msg.what == EVENT_DELAYED_FINISH) { - finish(); - } else { - Log.wtf(TAG, "Unknown message id: " + msg.what); - } - } - }; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - String action = intent.getAction(); - - // This activity is only ever launched with the - // ACTION_SIP_SELECT_PHONE action. - if (!OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE.equals(action)) { - Log.wtf(TAG, "onCreate: got intent action '" + action + "', expected " - + OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE); - finish(); - return; - } - - // mIntent is a copy of the original CALL intent that started the - // whole outgoing-call sequence. This intent will ultimately be - // passed to CallController.placeCall() after displaying the SIP - // call options dialog (if necessary). - mIntent = (Intent) intent.getParcelableExtra(OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT); - if (mIntent == null) { - finish(); - return; - } - - // 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); - - // If we're trying to make a SIP call, return a SipPhone if one is - // available. - // - // - If it's a sip: URI, this is definitely a SIP call, regardless - // of whether the data is a SIP address or a regular phone - // number. - // - // - If this is a tel: URI but the data contains an "@" character - // (see PhoneNumberUtils.isUriNumber()) we consider that to be a - // SIP number too. - // - // TODO: Eventually we may want to disallow that latter case - // (e.g. "tel:foo@example.com"). - // - // TODO: We should also consider moving this logic into the - // CallManager, where it could be made more generic. - // (For example, each "telephony provider" could be allowed - // to register the URI scheme(s) that it can handle, and the - // CallManager would then find the best match for every - // outgoing call.) - - boolean voipSupported = PhoneUtils.isVoipSupported(); - if (DBG) Log.v(TAG, "voipSupported: " + voipSupported); - mSipProfileDb = new SipProfileDb(this); - mSipSharedPreferences = new SipSharedPreferences(this); - mCallOption = mSipSharedPreferences.getSipCallOption(); - if (DBG) Log.v(TAG, "Call option: " + mCallOption); - Uri uri = mIntent.getData(); - String scheme = uri.getScheme(); - mNumber = PhoneNumberUtils.getNumberFromIntent(mIntent, this); - boolean isInCellNetwork = PhoneGlobals.getInstance().phoneMgr.isRadioOn(); - boolean isKnownCallScheme = Constants.SCHEME_TEL.equals(scheme) - || Constants.SCHEME_SIP.equals(scheme); - boolean isRegularCall = Constants.SCHEME_TEL.equals(scheme) - && !PhoneNumberUtils.isUriNumber(mNumber); - - // Bypass the handler if the call scheme is not sip or tel. - if (!isKnownCallScheme) { - setResultAndFinish(); - return; - } - - // Check if VoIP feature is supported. - if (!voipSupported) { - if (!isRegularCall) { - showDialog(DIALOG_NO_VOIP); - } - return; - } - - // Since we are not sure if anyone has touched the number during - // the NEW_OUTGOING_CALL broadcast, we just check if the provider - // put their gateway information in the intent. If so, it means - // someone has changed the destination number. We then make the - // call via the default pstn network. However, if one just alters - // the destination directly, then we still let it go through the - // Internet call option process. - if (!CallGatewayManager.hasPhoneProviderExtras(mIntent)) { - if (!isNetworkConnected()) { - if (!isRegularCall) { - showDialog(DIALOG_NO_INTERNET_ERROR); - return; - } - } else { - if (mCallOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME) - && isRegularCall && isInCellNetwork) { - showDialog(DIALOG_SELECT_PHONE_TYPE); - return; - } - if (!mCallOption.equals(Settings.System.SIP_ADDRESS_ONLY) - || !isRegularCall) { - mUseSipPhone = true; - } - } - } - - if (mUseSipPhone) { - // If there is no sip profile and it is a regular call, then we - // should use pstn network instead. - if ((mSipProfileDb.getProfilesCount() > 0) || !isRegularCall) { - startGetPrimarySipPhoneThread(); - return; - } else { - mUseSipPhone = false; - } - setResultAndFinish(); - } - } - - /** - * Starts a delayed finish() in order to give the UI - * some time to start up. - */ - private void startDelayedFinish() { - mHandler.sendEmptyMessageDelayed(EVENT_DELAYED_FINISH, DELAYED_FINISH_TIME); - } - - @Override - public void onPause() { - super.onPause(); - if (isFinishing()) return; - for (Dialog dialog : mDialogs) { - if (dialog != null) dialog.dismiss(); - } - finish(); - } - - protected Dialog onCreateDialog(int id) { - 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_OUTGOING_SIP_PHONE: - dialog = new AlertDialog.Builder(this) - .setTitle(R.string.pick_outgoing_sip_phone) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setSingleChoiceItems(getProfileNameArray(), -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; - case DIALOG_SELECT_WIFI_CALL: - dialog = new AlertDialog.Builder(this) - .setMessage(R.string.choose_wifi_for_call_msg) - .setPositiveButton(R.string.choose_wifi_for_call_yes, this) - .setNegativeButton(R.string.choose_wifi_for_call_no, this) - .setCancelable(false) - .create(); - break; - default: - dialog = null; - } - if (dialog != null) { - mDialogs[id] = dialog; - } - return dialog; - } - - 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); - } - - private CharSequence[] getProfileNameArray() { - CharSequence[] entries = new CharSequence[mProfileList.size()]; - int i = 0; - for (SipProfile p : mProfileList) { - entries[i++] = p.getProfileName(); - } - return entries; - } - - public void onClick(DialogInterface dialog, int id) { - if(dialog == mDialogs[DIALOG_SELECT_WIFI_CALL]) { - if (id == DialogInterface.BUTTON_NEGATIVE) { - setResultAndFinish(); - } - return; - } else if (id == DialogInterface.BUTTON_NEGATIVE) { - // button negative is cancel - finish(); - return; - } else if(dialog == mDialogs[DIALOG_SELECT_PHONE_TYPE]) { - String selection = getResources().getStringArray( - R.array.phone_type_values)[id]; - if (DBG) Log.v(TAG, "User pick phone " + selection); - if (selection.equals(getString(R.string.internet_phone))) { - mUseSipPhone = true; - startGetPrimarySipPhoneThread(); - return; - } - } else if (dialog == mDialogs[DIALOG_SELECT_OUTGOING_SIP_PHONE]) { - mOutgoingSipProfile = mProfileList.get(id); - } else if ((dialog == mDialogs[DIALOG_NO_INTERNET_ERROR]) - || (dialog == mDialogs[DIALOG_NO_VOIP])) { - finish(); - return; - } else { - if (id == DialogInterface.BUTTON_POSITIVE) { - // Redirect to sip settings and drop the call. - Intent newIntent = new Intent(this, SipSettings.class); - newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(newIntent); - } - finish(); - return; - } - setResultAndFinish(); - } - - public void onCancel(DialogInterface dialog) { - finish(); - } - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mMakePrimary = isChecked; - if (isChecked) { - mUnsetPriamryHint.setVisibility(View.VISIBLE); - } else { - mUnsetPriamryHint.setVisibility(View.INVISIBLE); - } - } - - private void createSipPhoneIfNeeded(SipProfile p) { - CallManager cm = PhoneGlobals.getInstance().mCM; - if (PhoneUtils.getSipPhoneFromUri(cm, p.getUriString()) != null) return; - - // Create the phone since we can not find it in CallManager - try { - SipManager.newInstance(this).open(p); - Phone phone = PhoneFactory.makeSipPhone(p.getUriString()); - if (phone != null) { - cm.registerPhone(phone); - } else { - Log.e(TAG, "cannot make sipphone profile" + p); - } - } catch (SipException e) { - Log.e(TAG, "cannot open sip profile" + p, e); - } - } - - private void setResultAndFinish() { - runOnUiThread(new Runnable() { - public void run() { - if (mOutgoingSipProfile != null) { - if (!isNetworkConnected()) { - showDialog(DIALOG_NO_INTERNET_ERROR); - return; - } - if (DBG) Log.v(TAG, "primary SIP URI is " + - mOutgoingSipProfile.getUriString()); - createSipPhoneIfNeeded(mOutgoingSipProfile); - mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI, - mOutgoingSipProfile.getUriString()); - if (mMakePrimary) { - mSipSharedPreferences.setPrimaryAccount( - mOutgoingSipProfile.getUriString()); - } - } - - if (mUseSipPhone && mOutgoingSipProfile == null) { - showDialog(DIALOG_START_SIP_SETTINGS); - return; - } else { - // Woo hoo -- it's finally OK to initiate the outgoing call! - PhoneGlobals.getInstance().callController.placeCall(mIntent); - } - startDelayedFinish(); - } - }); - } - - private boolean isNetworkConnected() { - ConnectivityManager cm = (ConnectivityManager) getSystemService( - Context.CONNECTIVITY_SERVICE); - if (cm != null) { - NetworkInfo ni = cm.getActiveNetworkInfo(); - if ((ni == null) || !ni.isConnected()) return false; - - return ((ni.getType() == ConnectivityManager.TYPE_WIFI) - || !SipManager.isSipWifiOnly(this)); - } - return false; - } - - private void startGetPrimarySipPhoneThread() { - new Thread(new Runnable() { - public void run() { - getPrimarySipPhone(); - } - }).start(); - } - - private void getPrimarySipPhone() { - String primarySipUri = mSipSharedPreferences.getPrimaryAccount(); - - mOutgoingSipProfile = getPrimaryFromExistingProfiles(primarySipUri); - if (mOutgoingSipProfile == null) { - if ((mProfileList != null) && (mProfileList.size() > 0)) { - runOnUiThread(new Runnable() { - public void run() { - showDialog(DIALOG_SELECT_OUTGOING_SIP_PHONE); - } - }); - return; - } - } - setResultAndFinish(); - } - - private SipProfile getPrimaryFromExistingProfiles(String primarySipUri) { - mProfileList = mSipProfileDb.retrieveSipProfileList(); - if (mProfileList == null) return null; - for (SipProfile p : mProfileList) { - if (p.getUriString().equals(primarySipUri)) return p; - } - return null; - } - - private boolean isConnectedToWifi() { - ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - if (cm != null) { - NetworkInfo ni = cm.getActiveNetworkInfo(); - return ni != null && ni.isConnected() && ni.getType() == ConnectivityManager.TYPE_WIFI; - } - return false; - } -} diff --git a/src/com/android/phone/SipUtil.java b/src/com/android/phone/SipUtil.java deleted file mode 100644 index a901d5850..000000000 --- a/src/com/android/phone/SipUtil.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 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.phone; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.net.sip.SipManager; - -public class SipUtil { - private SipUtil() { - } - - public static PendingIntent createIncomingCallPendingIntent() { - Context phoneContext = PhoneGlobals.getInstance(); - Intent intent = new Intent(phoneContext, SipBroadcastReceiver.class); - intent.setAction(SipManager.ACTION_SIP_INCOMING_CALL); - return PendingIntent.getBroadcast(phoneContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT); - } -} diff --git a/src/com/android/phone/sip/SipEditor.java b/src/com/android/phone/sip/SipEditor.java deleted file mode 100644 index 8145c94a3..000000000 --- a/src/com/android/phone/sip/SipEditor.java +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (C) 2010 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.phone.sip; - -import com.android.internal.telephony.CallManager; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; -import com.android.phone.R; -import com.android.phone.SipUtil; - -import android.app.ActionBar; -import android.app.AlertDialog; -import android.content.Intent; -import android.net.sip.SipManager; -import android.net.sip.SipProfile; -import android.os.Bundle; -import android.os.Parcelable; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceGroup; -import android.text.TextUtils; -import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.Toast; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.Arrays; - -/** - * The activity class for editing a new or existing SIP profile. - */ -public class SipEditor extends PreferenceActivity - implements Preference.OnPreferenceChangeListener { - private static final int MENU_SAVE = Menu.FIRST; - private static final int MENU_DISCARD = Menu.FIRST + 1; - private static final int MENU_REMOVE = Menu.FIRST + 2; - - private static final String TAG = SipEditor.class.getSimpleName(); - private static final String KEY_PROFILE = "profile"; - private static final String GET_METHOD_PREFIX = "get"; - private static final char SCRAMBLED = '*'; - private static final int NA = 0; - - private PrimaryAccountSelector mPrimaryAccountSelector; - private AdvancedSettings mAdvancedSettings; - private SipSharedPreferences mSharedPreferences; - private boolean mDisplayNameSet; - private boolean mHomeButtonClicked; - private boolean mUpdateRequired; - private boolean mUpdatedFieldIsEmpty; - - private SipManager mSipManager; - private SipProfileDb mProfileDb; - private SipProfile mOldProfile; - private CallManager mCallManager; - private Button mRemoveButton; - - enum PreferenceKey { - Username(R.string.username, 0, R.string.default_preference_summary), - Password(R.string.password, 0, R.string.default_preference_summary), - DomainAddress(R.string.domain_address, 0, R.string.default_preference_summary), - DisplayName(R.string.display_name, 0, R.string.display_name_summary), - ProxyAddress(R.string.proxy_address, 0, R.string.optional_summary), - Port(R.string.port, R.string.default_port, R.string.default_port), - Transport(R.string.transport, R.string.default_transport, NA), - SendKeepAlive(R.string.send_keepalive, R.string.sip_system_decide, NA), - AuthUserName(R.string.auth_username, 0, R.string.optional_summary); - - final int text; - final int initValue; - final int defaultSummary; - Preference preference; - - /** - * @param key The key name of the preference. - * @param initValue The initial value of the preference. - * @param defaultSummary The default summary value of the preference - * when the preference value is empty. - */ - PreferenceKey(int text, int initValue, int defaultSummary) { - this.text = text; - this.initValue = initValue; - this.defaultSummary = defaultSummary; - } - - String getValue() { - if (preference instanceof EditTextPreference) { - return ((EditTextPreference) preference).getText(); - } else if (preference instanceof ListPreference) { - return ((ListPreference) preference).getValue(); - } - throw new RuntimeException("getValue() for the preference " + this); - } - - void setValue(String value) { - if (preference instanceof EditTextPreference) { - String oldValue = getValue(); - ((EditTextPreference) preference).setText(value); - if (this != Password) { - Log.v(TAG, this + ": setValue() " + value + ": " + oldValue - + " --> " + getValue()); - } - } else if (preference instanceof ListPreference) { - ((ListPreference) preference).setValue(value); - } - - if (TextUtils.isEmpty(value)) { - preference.setSummary(defaultSummary); - } else if (this == Password) { - preference.setSummary(scramble(value)); - } else if ((this == DisplayName) - && value.equals(getDefaultDisplayName())) { - preference.setSummary(defaultSummary); - } else { - preference.setSummary(value); - } - } - } - - @Override - public void onResume() { - super.onResume(); - mHomeButtonClicked = false; - if (mCallManager.getState() != PhoneConstants.State.IDLE) { - mAdvancedSettings.show(); - getPreferenceScreen().setEnabled(false); - if (mRemoveButton != null) mRemoveButton.setEnabled(false); - } else { - getPreferenceScreen().setEnabled(true); - if (mRemoveButton != null) mRemoveButton.setEnabled(true); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - Log.v(TAG, "start profile editor"); - super.onCreate(savedInstanceState); - - 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); - - SipProfile p = mOldProfile = (SipProfile) ((savedInstanceState == null) - ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE) - : savedInstanceState.getParcelable(KEY_PROFILE)); - - PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); - for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) { - setupPreference(screen.getPreference(i)); - } - - if (p == null) { - screen.setTitle(R.string.sip_edit_new_title); - } - - mAdvancedSettings = new AdvancedSettings(); - mPrimaryAccountSelector = new PrimaryAccountSelector(p); - - loadPreferencesFromProfile(p); - } - - @Override - public void onPause() { - Log.v(TAG, "SipEditor onPause(): finishing? " + isFinishing()); - if (!isFinishing()) { - mHomeButtonClicked = true; - validateAndSetResult(); - } - super.onPause(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, MENU_DISCARD, 0, R.string.sip_menu_discard) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - menu.add(0, MENU_SAVE, 0, R.string.sip_menu_save) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - menu.add(0, MENU_REMOVE, 0, R.string.remove_sip_account) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuItem removeMenu = menu.findItem(MENU_REMOVE); - removeMenu.setVisible(mOldProfile != null); - menu.findItem(MENU_SAVE).setEnabled(mUpdateRequired); - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_SAVE: - validateAndSetResult(); - return true; - - case MENU_DISCARD: - finish(); - return true; - - case MENU_REMOVE: { - setRemovedProfileAndFinish(); - return true; - } - } - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - validateAndSetResult(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - private void saveAndRegisterProfile(SipProfile p) throws IOException { - if (p == null) return; - mProfileDb.saveProfile(p); - if (p.getAutoRegistration() - || mSharedPreferences.isPrimaryAccount(p.getUriString())) { - try { - mSipManager.open(p, SipUtil.createIncomingCallPendingIntent(), - null); - } catch (Exception e) { - Log.e(TAG, "register failed: " + p.getUriString(), e); - } - } - } - - private void deleteAndUnregisterProfile(SipProfile p) { - if (p == null) return; - mProfileDb.deleteProfile(p); - unregisterProfile(p.getUriString()); - } - - private void unregisterProfile(String uri) { - try { - mSipManager.close(uri); - } catch (Exception e) { - Log.e(TAG, "unregister failed: " + uri, e); - } - } - - private void setRemovedProfileAndFinish() { - Intent intent = new Intent(this, SipSettings.class); - setResult(RESULT_FIRST_USER, intent); - Toast.makeText(this, R.string.removing_account, Toast.LENGTH_SHORT) - .show(); - replaceProfile(mOldProfile, null); - // do finish() in replaceProfile() in a background thread - } - - private void showAlert(Throwable e) { - String msg = e.getMessage(); - if (TextUtils.isEmpty(msg)) msg = e.toString(); - showAlert(msg); - } - - private void showAlert(final String message) { - if (mHomeButtonClicked) { - Log.v(TAG, "Home button clicked, don't show dialog: " + message); - return; - } - runOnUiThread(new Runnable() { - @Override - public void run() { - new AlertDialog.Builder(SipEditor.this) - .setTitle(android.R.string.dialog_alert_title) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setMessage(message) - .setPositiveButton(R.string.alert_dialog_ok, null) - .show(); - } - }); - } - - private boolean isEditTextEmpty(PreferenceKey key) { - EditTextPreference pref = (EditTextPreference) key.preference; - return TextUtils.isEmpty(pref.getText()) - || pref.getSummary().equals(getString(key.defaultSummary)); - } - - private void validateAndSetResult() { - boolean allEmpty = true; - CharSequence firstEmptyFieldTitle = null; - for (PreferenceKey key : PreferenceKey.values()) { - Preference p = key.preference; - if (p instanceof EditTextPreference) { - EditTextPreference pref = (EditTextPreference) p; - boolean fieldEmpty = isEditTextEmpty(key); - if (allEmpty && !fieldEmpty) allEmpty = false; - - // use default value if display name is empty - if (fieldEmpty) { - switch (key) { - case DisplayName: - pref.setText(getDefaultDisplayName()); - break; - case AuthUserName: - case ProxyAddress: - // optional; do nothing - break; - case Port: - pref.setText(getString(R.string.default_port)); - break; - default: - if (firstEmptyFieldTitle == null) { - firstEmptyFieldTitle = pref.getTitle(); - } - } - } else if (key == PreferenceKey.Port) { - int port = Integer.parseInt(PreferenceKey.Port.getValue()); - if ((port < 1000) || (port > 65534)) { - showAlert(getString(R.string.not_a_valid_port)); - return; - } - } - } - } - - if (allEmpty || !mUpdateRequired) { - finish(); - return; - } else if (firstEmptyFieldTitle != null) { - showAlert(getString(R.string.empty_alert, firstEmptyFieldTitle)); - return; - } - try { - SipProfile profile = createSipProfile(); - Intent intent = new Intent(this, SipSettings.class); - intent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) profile); - setResult(RESULT_OK, intent); - Toast.makeText(this, R.string.saving_account, Toast.LENGTH_SHORT) - .show(); - - replaceProfile(mOldProfile, profile); - // do finish() in replaceProfile() in a background thread - } catch (Exception e) { - Log.w(TAG, "Can not create new SipProfile", e); - showAlert(e); - } - } - - private void unregisterOldPrimaryAccount() { - String primaryAccountUri = mSharedPreferences.getPrimaryAccount(); - Log.v(TAG, "old primary: " + primaryAccountUri); - if ((primaryAccountUri != null) - && !mSharedPreferences.isReceivingCallsEnabled()) { - Log.v(TAG, "unregister old primary: " + primaryAccountUri); - unregisterProfile(primaryAccountUri); - } - } - - private void replaceProfile(final SipProfile oldProfile, - final SipProfile newProfile) { - // Replace profile in a background thread as it takes time to access the - // storage; do finish() once everything goes fine. - // newProfile may be null if the old profile is to be deleted rather - // than being modified. - new Thread(new Runnable() { - public void run() { - try { - // if new profile is primary, unregister the old primary account - if ((newProfile != null) && mPrimaryAccountSelector.isSelected()) { - unregisterOldPrimaryAccount(); - } - - mPrimaryAccountSelector.commit(newProfile); - deleteAndUnregisterProfile(oldProfile); - saveAndRegisterProfile(newProfile); - finish(); - } catch (Exception e) { - Log.e(TAG, "Can not save/register new SipProfile", e); - showAlert(e); - } - } - }, "SipEditor").start(); - } - - private String getProfileName() { - return PreferenceKey.Username.getValue() + "@" - + PreferenceKey.DomainAddress.getValue(); - } - - private SipProfile createSipProfile() throws Exception { - return new SipProfile.Builder( - PreferenceKey.Username.getValue(), - PreferenceKey.DomainAddress.getValue()) - .setProfileName(getProfileName()) - .setPassword(PreferenceKey.Password.getValue()) - .setOutboundProxy(PreferenceKey.ProxyAddress.getValue()) - .setProtocol(PreferenceKey.Transport.getValue()) - .setDisplayName(PreferenceKey.DisplayName.getValue()) - .setPort(Integer.parseInt(PreferenceKey.Port.getValue())) - .setSendKeepAlive(isAlwaysSendKeepAlive()) - .setAutoRegistration( - mSharedPreferences.isReceivingCallsEnabled()) - .setAuthUserName(PreferenceKey.AuthUserName.getValue()) - .build(); - } - - public boolean onPreferenceChange(Preference pref, Object newValue) { - if (!mUpdateRequired) { - mUpdateRequired = true; - if (mOldProfile != null) { - unregisterProfile(mOldProfile.getUriString()); - } - } - if (pref instanceof CheckBoxPreference) { - invalidateOptionsMenu(); - return true; - } - String value = (newValue == null) ? "" : newValue.toString(); - if (TextUtils.isEmpty(value)) { - pref.setSummary(getPreferenceKey(pref).defaultSummary); - } else if (pref == PreferenceKey.Password.preference) { - pref.setSummary(scramble(value)); - } else { - pref.setSummary(value); - } - - if (pref == PreferenceKey.DisplayName.preference) { - ((EditTextPreference) pref).setText(value); - checkIfDisplayNameSet(); - } - - // SAVE menu should be enabled once the user modified some preference. - invalidateOptionsMenu(); - return true; - } - - private PreferenceKey getPreferenceKey(Preference pref) { - for (PreferenceKey key : PreferenceKey.values()) { - if (key.preference == pref) return key; - } - throw new RuntimeException("not possible to reach here"); - } - - private void loadPreferencesFromProfile(SipProfile p) { - if (p != null) { - Log.v(TAG, "Edit the existing profile : " + p.getProfileName()); - try { - Class profileClass = SipProfile.class; - for (PreferenceKey key : PreferenceKey.values()) { - Method meth = profileClass.getMethod(GET_METHOD_PREFIX - + getString(key.text), (Class[])null); - if (key == PreferenceKey.SendKeepAlive) { - boolean value = ((Boolean) - meth.invoke(p, (Object[]) null)).booleanValue(); - key.setValue(getString(value - ? R.string.sip_always_send_keepalive - : R.string.sip_system_decide)); - } else { - Object value = meth.invoke(p, (Object[])null); - key.setValue((value == null) ? "" : value.toString()); - } - } - checkIfDisplayNameSet(); - } catch (Exception e) { - Log.e(TAG, "Can not load pref from profile", e); - } - } else { - Log.v(TAG, "Edit a new profile"); - for (PreferenceKey key : PreferenceKey.values()) { - key.preference.setOnPreferenceChangeListener(this); - - // FIXME: android:defaultValue in preference xml file doesn't - // work. Even if we setValue() for each preference in the case - // of (p != null), the dialog still shows android:defaultValue, - // not the value set by setValue(). This happens if - // android:defaultValue is not empty. Is it a bug? - if (key.initValue != 0) { - key.setValue(getString(key.initValue)); - } - } - mDisplayNameSet = false; - } - } - - private boolean isAlwaysSendKeepAlive() { - ListPreference pref = (ListPreference) - PreferenceKey.SendKeepAlive.preference; - return getString(R.string.sip_always_send_keepalive).equals( - pref.getValue()); - } - - private void setCheckBox(PreferenceKey key, boolean checked) { - CheckBoxPreference pref = (CheckBoxPreference) key.preference; - pref.setChecked(checked); - } - - private void setupPreference(Preference pref) { - pref.setOnPreferenceChangeListener(this); - for (PreferenceKey key : PreferenceKey.values()) { - String name = getString(key.text); - if (name.equals(pref.getKey())) { - key.preference = pref; - return; - } - } - } - - private void checkIfDisplayNameSet() { - String displayName = PreferenceKey.DisplayName.getValue(); - mDisplayNameSet = !TextUtils.isEmpty(displayName) - && !displayName.equals(getDefaultDisplayName()); - Log.d(TAG, "displayName set? " + mDisplayNameSet); - if (mDisplayNameSet) { - PreferenceKey.DisplayName.preference.setSummary(displayName); - } else { - PreferenceKey.DisplayName.setValue(""); - } - } - - private static String getDefaultDisplayName() { - return PreferenceKey.Username.getValue(); - } - - private static String scramble(String s) { - char[] cc = new char[s.length()]; - Arrays.fill(cc, SCRAMBLED); - return new String(cc); - } - - // only takes care of the primary account setting in SipSharedSettings - private class PrimaryAccountSelector { - private CheckBoxPreference mCheckbox; - private final boolean mWasPrimaryAccount; - - // @param profile profile to be edited; null if adding new profile - PrimaryAccountSelector(SipProfile profile) { - mCheckbox = (CheckBoxPreference) getPreferenceScreen() - .findPreference(getString(R.string.set_primary)); - boolean noPrimaryAccountSet = - !mSharedPreferences.hasPrimaryAccount(); - boolean editNewProfile = (profile == null); - mWasPrimaryAccount = !editNewProfile - && mSharedPreferences.isPrimaryAccount( - profile.getUriString()); - - Log.v(TAG, " noPrimaryAccountSet: " + noPrimaryAccountSet); - Log.v(TAG, " editNewProfile: " + editNewProfile); - Log.v(TAG, " mWasPrimaryAccount: " + mWasPrimaryAccount); - - mCheckbox.setChecked(mWasPrimaryAccount - || (editNewProfile && noPrimaryAccountSet)); - } - - boolean isSelected() { - return mCheckbox.isChecked(); - } - - // profile is null if the user removes it - void commit(SipProfile profile) { - if ((profile != null) && mCheckbox.isChecked()) { - mSharedPreferences.setPrimaryAccount(profile.getUriString()); - } else if (mWasPrimaryAccount) { - mSharedPreferences.unsetPrimaryAccount(); - } - Log.d(TAG, " primary account changed to : " - + mSharedPreferences.getPrimaryAccount()); - } - } - - private class AdvancedSettings - implements Preference.OnPreferenceClickListener { - private Preference mAdvancedSettingsTrigger; - private Preference[] mPreferences; - private boolean mShowing = false; - - AdvancedSettings() { - mAdvancedSettingsTrigger = getPreferenceScreen().findPreference( - getString(R.string.advanced_settings)); - mAdvancedSettingsTrigger.setOnPreferenceClickListener(this); - - loadAdvancedPreferences(); - } - - private void loadAdvancedPreferences() { - PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); - - addPreferencesFromResource(R.xml.sip_advanced_edit); - PreferenceGroup group = (PreferenceGroup) screen.findPreference( - getString(R.string.advanced_settings_container)); - screen.removePreference(group); - - mPreferences = new Preference[group.getPreferenceCount()]; - int order = screen.getPreferenceCount(); - for (int i = 0, n = mPreferences.length; i < n; i++) { - Preference pref = group.getPreference(i); - pref.setOrder(order++); - setupPreference(pref); - mPreferences[i] = pref; - } - } - - void show() { - mShowing = true; - mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_hide); - PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); - for (Preference pref : mPreferences) { - screen.addPreference(pref); - Log.v(TAG, "add pref " + pref.getKey() + ": order=" + pref.getOrder()); - } - } - - private void hide() { - mShowing = false; - mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_show); - PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen(); - for (Preference pref : mPreferences) { - screen.removePreference(pref); - } - } - - public boolean onPreferenceClick(Preference preference) { - Log.v(TAG, "optional settings clicked"); - if (!mShowing) { - show(); - } else { - hide(); - } - return true; - } - } -} diff --git a/src/com/android/phone/sip/SipProfileDb.java b/src/com/android/phone/sip/SipProfileDb.java deleted file mode 100644 index a51dfb955..000000000 --- a/src/com/android/phone/sip/SipProfileDb.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2010 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.phone.sip; - -import com.android.internal.os.AtomicFile; - -import android.content.Context; -import android.net.sip.SipProfile; -import android.util.Log; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Utility class that helps perform operations on the SipProfile database. - */ -public class SipProfileDb { - private static final String TAG = SipProfileDb.class.getSimpleName(); - - private static final String PROFILES_DIR = "/profiles/"; - private static final String PROFILE_OBJ_FILE = ".pobj"; - - private String mProfilesDirectory; - private SipSharedPreferences mSipSharedPreferences; - private int mProfilesCount = -1; - - public SipProfileDb(Context context) { - mProfilesDirectory = context.getFilesDir().getAbsolutePath() - + PROFILES_DIR; - mSipSharedPreferences = new SipSharedPreferences(context); - } - - public void deleteProfile(SipProfile p) { - synchronized(SipProfileDb.class) { - deleteProfile(new File(mProfilesDirectory + p.getProfileName())); - if (mProfilesCount < 0) retrieveSipProfileListInternal(); - mSipSharedPreferences.setProfilesCount(--mProfilesCount); - } - } - - private void deleteProfile(File file) { - if (file.isDirectory()) { - for (File child : file.listFiles()) deleteProfile(child); - } - file.delete(); - } - - public void saveProfile(SipProfile p) throws IOException { - synchronized(SipProfileDb.class) { - if (mProfilesCount < 0) retrieveSipProfileListInternal(); - File f = new File(mProfilesDirectory + p.getProfileName()); - if (!f.exists()) f.mkdirs(); - AtomicFile atomicFile = - new AtomicFile(new File(f, PROFILE_OBJ_FILE)); - FileOutputStream fos = null; - ObjectOutputStream oos = null; - try { - fos = atomicFile.startWrite(); - oos = new ObjectOutputStream(fos); - oos.writeObject(p); - oos.flush(); - mSipSharedPreferences.setProfilesCount(++mProfilesCount); - atomicFile.finishWrite(fos); - } catch (IOException e) { - atomicFile.failWrite(fos); - throw e; - } finally { - if (oos != null) oos.close(); - } - } - } - - public int getProfilesCount() { - return (mProfilesCount < 0) ? - mSipSharedPreferences.getProfilesCount() : mProfilesCount; - } - - public List retrieveSipProfileList() { - synchronized(SipProfileDb.class) { - return retrieveSipProfileListInternal(); - } - } - - private List retrieveSipProfileListInternal() { - List sipProfileList = Collections.synchronizedList( - new ArrayList()); - - File root = new File(mProfilesDirectory); - String[] dirs = root.list(); - if (dirs == null) return sipProfileList; - for (String dir : dirs) { - File f = new File(new File(root, dir), PROFILE_OBJ_FILE); - if (!f.exists()) continue; - try { - SipProfile p = deserialize(f); - if (p == null) continue; - if (!dir.equals(p.getProfileName())) continue; - - sipProfileList.add(p); - } catch (IOException e) { - Log.e(TAG, "retrieveProfileListFromStorage()", e); - } - } - mProfilesCount = sipProfileList.size(); - mSipSharedPreferences.setProfilesCount(mProfilesCount); - return sipProfileList; - } - - private SipProfile deserialize(File profileObjectFile) throws IOException { - AtomicFile atomicFile = new AtomicFile(profileObjectFile); - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(atomicFile.openRead()); - SipProfile p = (SipProfile) ois.readObject(); - return p; - } catch (ClassNotFoundException e) { - Log.w(TAG, "deserialize a profile: " + e); - } finally { - if (ois!= null) ois.close(); - } - return null; - } -} diff --git a/src/com/android/phone/sip/SipSettings.java b/src/com/android/phone/sip/SipSettings.java deleted file mode 100644 index d58386c11..000000000 --- a/src/com/android/phone/sip/SipSettings.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright (C) 2010 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.phone.sip; - -import com.android.internal.telephony.CallManager; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; -import com.android.phone.CallFeaturesSetting; -import com.android.phone.R; -import com.android.phone.SipUtil; - -import android.app.ActionBar; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.net.sip.SipErrorCode; -import android.net.sip.SipException; -import android.net.sip.SipManager; -import android.net.sip.SipProfile; -import android.net.sip.SipRegistrationListener; -import android.os.Bundle; -import android.os.Parcelable; -import android.os.Process; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.text.TextUtils; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; - -import java.io.IOException; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * The PreferenceActivity class for managing sip profile preferences. - */ -public class SipSettings extends PreferenceActivity { - public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; - - private static final int MENU_ADD_ACCOUNT = Menu.FIRST; - - static final String KEY_SIP_PROFILE = "sip_profile"; - - private static final String BUTTON_SIP_RECEIVE_CALLS = - "sip_receive_calls_key"; - private static final String PREF_SIP_LIST = "sip_account_list"; - private static final String TAG = "SipSettings"; - - private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1; - - private PackageManager mPackageManager; - private SipManager mSipManager; - private CallManager mCallManager; - private SipProfileDb mProfileDb; - - private SipProfile mProfile; // profile that's being edited - - private CheckBoxPreference mButtonSipReceiveCalls; - private PreferenceCategory mSipListContainer; - private Map mSipPreferenceMap; - private List mSipProfileList; - private SipSharedPreferences mSipSharedPreferences; - private int mUid = Process.myUid(); - - private class SipPreference extends Preference { - SipProfile mProfile; - SipPreference(Context c, SipProfile p) { - super(c); - setProfile(p); - } - - SipProfile getProfile() { - return mProfile; - } - - void setProfile(SipProfile p) { - mProfile = p; - setTitle(getProfileName(p)); - updateSummary(mSipSharedPreferences.isReceivingCallsEnabled() - ? getString(R.string.registration_status_checking_status) - : getString(R.string.registration_status_not_receiving)); - } - - void updateSummary(String registrationStatus) { - int profileUid = mProfile.getCallingUid(); - boolean isPrimary = mProfile.getUriString().equals( - mSipSharedPreferences.getPrimaryAccount()); - Log.v(TAG, "profile uid is " + profileUid + " isPrimary:" - + isPrimary + " registration:" + registrationStatus - + " Primary:" + mSipSharedPreferences.getPrimaryAccount() - + " status:" + registrationStatus); - String summary = ""; - if ((profileUid > 0) && (profileUid != mUid)) { - // from third party apps - summary = getString(R.string.third_party_account_summary, - getPackageNameFromUid(profileUid)); - } else if (isPrimary) { - summary = getString(R.string.primary_account_summary_with, - registrationStatus); - } else { - summary = registrationStatus; - } - setSummary(summary); - } - } - - private String getPackageNameFromUid(int uid) { - try { - String[] pkgs = mPackageManager.getPackagesForUid(uid); - ApplicationInfo ai = - mPackageManager.getApplicationInfo(pkgs[0], 0); - return ai.loadLabel(mPackageManager).toString(); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "cannot find name of uid " + uid, e); - } - return "uid:" + uid; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mSipManager = SipManager.newInstance(this); - mSipSharedPreferences = new SipSharedPreferences(this); - mProfileDb = new SipProfileDb(this); - - mPackageManager = getPackageManager(); - setContentView(R.layout.sip_settings_ui); - addPreferencesFromResource(R.xml.sip_setting); - mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST); - registerForReceiveCallsCheckBox(); - mCallManager = CallManager.getInstance(); - - updateProfilesStatus(); - - ActionBar actionBar = getActionBar(); - if (actionBar != null) { - // android.R.id.home will be triggered in onOptionsItemSelected() - actionBar.setDisplayHomeAsUpEnabled(true); - } - } - - @Override - public void onResume() { - super.onResume(); - - if (mCallManager.getState() != PhoneConstants.State.IDLE) { - mButtonSipReceiveCalls.setEnabled(false); - } else { - mButtonSipReceiveCalls.setEnabled(true); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - unregisterForContextMenu(getListView()); - } - - @Override - protected void onActivityResult(final int requestCode, final int resultCode, - final Intent intent) { - if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return; - new Thread() { - @Override - public void run() { - try { - if (mProfile != null) { - Log.v(TAG, "Removed Profile:" + mProfile.getProfileName()); - deleteProfile(mProfile); - } - - SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE); - if (resultCode == RESULT_OK) { - Log.v(TAG, "New Profile Name:" + profile.getProfileName()); - addProfile(profile); - } - updateProfilesStatus(); - } catch (IOException e) { - Log.v(TAG, "Can not handle the profile : " + e.getMessage()); - } - } - }.start(); - } - - private void registerForReceiveCallsCheckBox() { - mButtonSipReceiveCalls = (CheckBoxPreference) findPreference - (BUTTON_SIP_RECEIVE_CALLS); - mButtonSipReceiveCalls.setChecked( - mSipSharedPreferences.isReceivingCallsEnabled()); - mButtonSipReceiveCalls.setOnPreferenceClickListener( - new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - final boolean enabled = - ((CheckBoxPreference) preference).isChecked(); - new Thread(new Runnable() { - public void run() { - handleSipReceiveCallsOption(enabled); - } - }).start(); - return true; - } - }); - } - - private synchronized void handleSipReceiveCallsOption(boolean enabled) { - mSipSharedPreferences.setReceivingCallsEnabled(enabled); - List sipProfileList = mProfileDb.retrieveSipProfileList(); - for (SipProfile p : sipProfileList) { - String sipUri = p.getUriString(); - p = updateAutoRegistrationFlag(p, enabled); - try { - if (enabled) { - mSipManager.open(p, - SipUtil.createIncomingCallPendingIntent(), null); - } else { - mSipManager.close(sipUri); - if (mSipSharedPreferences.isPrimaryAccount(sipUri)) { - // re-open in order to make calls - mSipManager.open(p); - } - } - } catch (Exception e) { - Log.e(TAG, "register failed", e); - } - } - updateProfilesStatus(); - } - - private SipProfile updateAutoRegistrationFlag( - SipProfile p, boolean enabled) { - SipProfile newProfile = new SipProfile.Builder(p) - .setAutoRegistration(enabled) - .build(); - try { - mProfileDb.deleteProfile(p); - mProfileDb.saveProfile(newProfile); - } catch (Exception e) { - Log.e(TAG, "updateAutoRegistrationFlag error", e); - } - return newProfile; - } - - private void updateProfilesStatus() { - new Thread(new Runnable() { - @Override - public void run() { - try { - retrieveSipLists(); - } catch (Exception e) { - Log.e(TAG, "isRegistered", e); - } - } - }).start(); - } - - private String getProfileName(SipProfile profile) { - String profileName = profile.getProfileName(); - if (TextUtils.isEmpty(profileName)) { - profileName = profile.getUserName() + "@" + profile.getSipDomain(); - } - return profileName; - } - - private void retrieveSipLists() { - mSipPreferenceMap = new LinkedHashMap(); - mSipProfileList = mProfileDb.retrieveSipProfileList(); - processActiveProfilesFromSipService(); - Collections.sort(mSipProfileList, new Comparator() { - @Override - public int compare(SipProfile p1, SipProfile p2) { - return getProfileName(p1).compareTo(getProfileName(p2)); - } - - public boolean equals(SipProfile p) { - // not used - return false; - } - }); - mSipListContainer.removeAll(); - for (SipProfile p : mSipProfileList) { - addPreferenceFor(p); - } - - if (!mSipSharedPreferences.isReceivingCallsEnabled()) return; - for (SipProfile p : mSipProfileList) { - if (mUid == p.getCallingUid()) { - try { - mSipManager.setRegistrationListener( - p.getUriString(), createRegistrationListener()); - } catch (SipException e) { - Log.e(TAG, "cannot set registration listener", e); - } - } - } - } - - private void processActiveProfilesFromSipService() { - SipProfile[] activeList = mSipManager.getListOfProfiles(); - for (SipProfile activeProfile : activeList) { - SipProfile profile = getProfileFromList(activeProfile); - if (profile == null) { - mSipProfileList.add(activeProfile); - } else { - profile.setCallingUid(activeProfile.getCallingUid()); - } - } - } - - private SipProfile getProfileFromList(SipProfile activeProfile) { - for (SipProfile p : mSipProfileList) { - if (p.getUriString().equals(activeProfile.getUriString())) { - return p; - } - } - return null; - } - - private void addPreferenceFor(SipProfile p) { - String status; - Log.v(TAG, "addPreferenceFor profile uri" + p.getUri()); - SipPreference pref = new SipPreference(this, p); - mSipPreferenceMap.put(p.getUriString(), pref); - mSipListContainer.addPreference(pref); - - pref.setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference pref) { - handleProfileClick(((SipPreference) pref).mProfile); - return true; - } - }); - } - - private void handleProfileClick(final SipProfile profile) { - int uid = profile.getCallingUid(); - if (uid == mUid || uid == 0) { - startSipEditor(profile); - return; - } - new AlertDialog.Builder(this) - .setTitle(R.string.alert_dialog_close) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setPositiveButton(R.string.close_profile, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int w) { - deleteProfile(profile); - unregisterProfile(profile); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - } - - private void unregisterProfile(final SipProfile p) { - // run it on background thread for better UI response - new Thread(new Runnable() { - @Override - public void run() { - try { - mSipManager.close(p.getUriString()); - } catch (Exception e) { - Log.e(TAG, "unregister failed, SipService died?", e); - } - } - }, "unregisterProfile").start(); - } - - void deleteProfile(SipProfile p) { - mSipProfileList.remove(p); - SipPreference pref = mSipPreferenceMap.remove(p.getUriString()); - mSipListContainer.removePreference(pref); - } - - private void addProfile(SipProfile p) throws IOException { - try { - mSipManager.setRegistrationListener(p.getUriString(), - createRegistrationListener()); - } catch (Exception e) { - Log.e(TAG, "cannot set registration listener", e); - } - mSipProfileList.add(p); - addPreferenceFor(p); - } - - private void startSipEditor(final SipProfile profile) { - mProfile = profile; - Intent intent = new Intent(this, SipEditor.class); - intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile); - startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE); - } - - private void showRegistrationMessage(final String profileUri, - final String message) { - runOnUiThread(new Runnable() { - @Override - public void run() { - SipPreference pref = mSipPreferenceMap.get(profileUri); - if (pref != null) { - pref.updateSummary(message); - } - } - }); - } - - private SipRegistrationListener createRegistrationListener() { - return new SipRegistrationListener() { - @Override - public void onRegistrationDone(String profileUri, long expiryTime) { - showRegistrationMessage(profileUri, getString( - R.string.registration_status_done)); - } - - @Override - public void onRegistering(String profileUri) { - showRegistrationMessage(profileUri, getString( - R.string.registration_status_registering)); - } - - @Override - public void onRegistrationFailed(String profileUri, int errorCode, - String message) { - switch (errorCode) { - case SipErrorCode.IN_PROGRESS: - showRegistrationMessage(profileUri, getString( - R.string.registration_status_still_trying)); - break; - case SipErrorCode.INVALID_CREDENTIALS: - showRegistrationMessage(profileUri, getString( - R.string.registration_status_invalid_credentials)); - break; - case SipErrorCode.SERVER_UNREACHABLE: - showRegistrationMessage(profileUri, getString( - R.string.registration_status_server_unreachable)); - break; - case SipErrorCode.DATA_CONNECTION_LOST: - if (SipManager.isSipWifiOnly(getApplicationContext())){ - showRegistrationMessage(profileUri, getString( - R.string.registration_status_no_wifi_data)); - } else { - showRegistrationMessage(profileUri, getString( - R.string.registration_status_no_data)); - } - break; - case SipErrorCode.CLIENT_ERROR: - showRegistrationMessage(profileUri, getString( - R.string.registration_status_not_running)); - break; - default: - showRegistrationMessage(profileUri, getString( - R.string.registration_status_failed_try_later, - message)); - } - } - }; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_sip_account) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menu.findItem(MENU_ADD_ACCOUNT).setEnabled( - mCallManager.getState() == PhoneConstants.State.IDLE); - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - final int itemId = item.getItemId(); - switch (itemId) { - case android.R.id.home: { - CallFeaturesSetting.goUpToTopLevelSetting(this); - return true; - } - case MENU_ADD_ACCOUNT: { - startSipEditor(null); - return true; - } - } - return super.onOptionsItemSelected(item); - } -} diff --git a/src/com/android/phone/sip/SipSharedPreferences.java b/src/com/android/phone/sip/SipSharedPreferences.java deleted file mode 100644 index e15db64ed..000000000 --- a/src/com/android/phone/sip/SipSharedPreferences.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2010 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.phone.sip; - -import com.android.phone.R; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.SharedPreferences; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.text.TextUtils; -import android.util.Log; - -/** - * Wrapper for SIP's shared preferences. - */ -public class SipSharedPreferences { - private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; - private static final String KEY_PRIMARY_ACCOUNT = "primary"; - private static final String KEY_NUMBER_OF_PROFILES = "profiles"; - - private SharedPreferences mPreferences; - private Context mContext; - - public SipSharedPreferences(Context context) { - mPreferences = context.getSharedPreferences( - SIP_SHARED_PREFERENCES, Context.MODE_WORLD_READABLE); - mContext = context; - } - - public void setPrimaryAccount(String accountUri) { - SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(KEY_PRIMARY_ACCOUNT, accountUri); - editor.apply(); - } - - public void unsetPrimaryAccount() { - setPrimaryAccount(null); - } - - /** Returns the primary account URI or null if it does not exist. */ - public String getPrimaryAccount() { - return mPreferences.getString(KEY_PRIMARY_ACCOUNT, null); - } - - public boolean isPrimaryAccount(String accountUri) { - return accountUri.equals( - mPreferences.getString(KEY_PRIMARY_ACCOUNT, null)); - } - - public boolean hasPrimaryAccount() { - return !TextUtils.isEmpty( - mPreferences.getString(KEY_PRIMARY_ACCOUNT, null)); - } - - public void setProfilesCount(int number) { - SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(KEY_NUMBER_OF_PROFILES, number); - editor.apply(); - } - - public int getProfilesCount() { - return mPreferences.getInt(KEY_NUMBER_OF_PROFILES, 0); - } - - public void setSipCallOption(String option) { - Settings.System.putString(mContext.getContentResolver(), - Settings.System.SIP_CALL_OPTIONS, option); - } - - public String getSipCallOption() { - String option = Settings.System.getString(mContext.getContentResolver(), - Settings.System.SIP_CALL_OPTIONS); - return (option != null) ? option - : mContext.getString(R.string.sip_address_only); - } - - public void setReceivingCallsEnabled(boolean enabled) { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SIP_RECEIVE_CALLS, (enabled ? 1 : 0)); - } - - public boolean isReceivingCallsEnabled() { - try { - return (Settings.System.getInt(mContext.getContentResolver(), - Settings.System.SIP_RECEIVE_CALLS) != 0); - } catch (SettingNotFoundException e) { - Log.d("SIP", "ReceiveCall option is not set; use default value"); - return false; - } - } - - // TODO: back up to Android Backup -} diff --git a/src/com/android/services/telephony/SipConnection.java b/src/com/android/services/telephony/SipConnection.java deleted file mode 100644 index 133a42d05..000000000 --- a/src/com/android/services/telephony/SipConnection.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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; - -import android.telecomm.Connection; - -/** - * A {@link Connection} object for SIP calls. - */ -public class SipConnection extends TelephonyConnection { - - public SipConnection(com.android.internal.telephony.Connection connection) { - super(connection); - } - - // TODO: Fill in the below methods - - /** {@inheritDoc} */ - @Override - protected void onPlayDtmfTone(char c) { - super.onPlayDtmfTone(c); - } - - /** {@inheritDoc} */ - @Override - protected void onStopDtmfTone() { - super.onStopDtmfTone(); - } - - /** {@inheritDoc} */ - @Override - protected void onDisconnect() { - super.onDisconnect(); - } - - /** {@inheritDoc} */ - @Override - protected void onAbort() { - super.onAbort(); - } - - /** {@inheritDoc} */ - @Override - public void onHold() { - super.onHold(); - } - - /** {@inheritDoc} */ - @Override - protected void onUnhold() { - super.onUnhold(); - } - - /** {@inheritDoc} */ - @Override - protected void onAnswer() { - super.onAnswer(); - } - - /** {@inheritDoc} */ - @Override - protected void onReject() { - super.onReject(); - } -} diff --git a/src/com/android/services/telephony/SipConnectionService.java b/src/com/android/services/telephony/SipConnectionService.java deleted file mode 100644 index ea364bd4f..000000000 --- a/src/com/android/services/telephony/SipConnectionService.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.net.sip.SipException; -import android.net.sip.SipManager; -import android.net.sip.SipProfile; -import android.net.Uri; -import android.os.AsyncTask; -import android.provider.Settings; -import android.telephony.PhoneNumberUtils; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneFactory; -import com.android.internal.telephony.sip.SipPhone; -import com.android.phone.Constants; -import com.android.phone.PhoneUtils; -import com.android.phone.sip.SipProfileDb; -import com.android.phone.sip.SipSharedPreferences; -import android.telecomm.Connection; -import android.telecomm.ConnectionRequest; -import android.telecomm.Response; - -import java.util.HashMap; - -/** - * Connection service that uses the SIP phone. - */ -public class SipConnectionService extends TelephonyConnectionService { - private static HashMap sSipPhones = new HashMap(); - - /** {@inheritDoc} */ - @Override - public void onCreateConnections( - ConnectionRequest request, - OutgoingCallResponse callback) { - new GetSipProfileTask(this, request, callback).execute(); - super.onCreateConnections(request, callback); - } - - /** {@inheritDoc} */ - @Override - public void onCreateIncomingConnection( - ConnectionRequest request, - Response callback) { - super.onCreateIncomingConnection(request, callback); - // TODO: fill in - } - - /** {@inheritDoc} */ - @Override - protected boolean canCall(Uri handle) { - return shouldUseSipPhone(handle.getScheme(), handle.getSchemeSpecificPart()); - } - - /** {@inheritDoc} */ - @Override - protected TelephonyConnection onCreateTelephonyConnection( - ConnectionRequest request, - Phone phone, - com.android.internal.telephony.Connection connection) { - return new SipConnection(connection); - } - - private boolean shouldUseSipPhone(String scheme, String number) { - // Scheme must be "sip" or "tel". - boolean isKnownCallScheme = Constants.SCHEME_TEL.equals(scheme) - || Constants.SCHEME_SIP.equals(scheme); - if (!isKnownCallScheme) { - return false; - } - - // Is voip supported - boolean voipSupported = PhoneUtils.isVoipSupported(); - if (!voipSupported) { - return false; - } - - // Check SIP address only - SipSharedPreferences sipSharedPreferences = new SipSharedPreferences(this); - String callOption = sipSharedPreferences.getSipCallOption(); - boolean isRegularNumber = Constants.SCHEME_TEL.equals(scheme) - && !PhoneNumberUtils.isUriNumber(number); - if (callOption.equals(Settings.System.SIP_ADDRESS_ONLY) && isRegularNumber) { - return false; - } - - // Check if no SIP profiles. - SipProfileDb sipProfileDb = new SipProfileDb(this); - if (sipProfileDb.getProfilesCount() == 0 && isRegularNumber) { - return false; - } - - return true; - } - - /** - * Asynchronously looks up the SIP profile to use for the given call. - */ - private class GetSipProfileTask extends AsyncTask { - 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); - } - } - - private void onSipProfileChosen( - SipProfile profile, - ConnectionRequest request, - OutgoingCallResponse response) { - SipPhone phone = null; - if (profile != null) { - String sipUri = profile.getUriString(); - phone = sSipPhones.get(sipUri); - if (phone == null) { - try { - SipManager.newInstance(this).open(profile); - phone = (SipPhone) PhoneFactory.makeSipPhone(sipUri); - sSipPhones.put(sipUri, phone); - } catch (SipException e) { - Log.e(this, e, "Failed to make a SIP phone"); - } - } - } - startCallWithPhone(phone, request, response); - } -} diff --git a/src/com/android/services/telephony/TelephonyCallServiceProvider.java b/src/com/android/services/telephony/TelephonyCallServiceProvider.java index 12bb2afaf..5225759fa 100644 --- a/src/com/android/services/telephony/TelephonyCallServiceProvider.java +++ b/src/com/android/services/telephony/TelephonyCallServiceProvider.java @@ -34,11 +34,6 @@ public class TelephonyCallServiceProvider extends CallServiceProvider { .setConnectionService(PstnConnectionService.class) .setNetworkType(CallServiceDescriptor.FLAG_PSTN) .build()); - descriptors.add(CallServiceDescriptor.newBuilder(this) - .setConnectionService(SipConnectionService.class) - .setNetworkType(CallServiceDescriptor.FLAG_WIFI | - CallServiceDescriptor.FLAG_MOBILE) - .build()); response.setCallServiceDescriptors(descriptors); } } -- cgit v1.2.3