Merge "progress_large id defined incorrectly"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4f9dbfb..2721a8b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -538,7 +538,7 @@
</receiver>
<!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
- <receiver android:name="PhoneGlobals$NotificationBroadcastReceiver" exported="false">
+ <receiver android:name="PhoneGlobals$NotificationBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="com.android.phone.ACTION_HANG_UP_ONGOING_CALL" />
<action android:name="com.android.phone.ACTION_CALL_BACK_FROM_NOTIFICATION" />
diff --git a/common/src/com/android/services/telephony/common/Call.java b/common/src/com/android/services/telephony/common/Call.java
index 4a74114..9bcf127 100644
--- a/common/src/com/android/services/telephony/common/Call.java
+++ b/common/src/com/android/services/telephony/common/Call.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.DisconnectCause;
import com.android.internal.telephony.PhoneConstants;
import com.google.android.collect.Sets;
@@ -93,56 +94,6 @@
| RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE;
}
- /**
- * Copy of states found in Connection object since Connection object is not available to the UI
- * code.
- * TODO: Consider cutting this down to only the types used by the UI.
- * TODO: Consider adding a CUSTOM cause type and a customDisconnect member variable to
- * the Call object. This would allow OEMs to extend the cause list without
- * needing to alter our implementation.
- */
- public enum DisconnectCause {
- NOT_DISCONNECTED, /* has not yet disconnected */
- INCOMING_MISSED, /* an incoming call that was missed and never answered */
- NORMAL, /* normal; remote */
- LOCAL, /* normal; local hangup */
- BUSY, /* outgoing call to busy line */
- CONGESTION, /* outgoing call to congested network */
- MMI, /* not presently used; dial() returns null */
- INVALID_NUMBER, /* invalid dial string */
- NUMBER_UNREACHABLE, /* cannot reach the peer */
- SERVER_UNREACHABLE, /* cannot reach the server */
- INVALID_CREDENTIALS, /* invalid credentials */
- OUT_OF_NETWORK, /* calling from out of network is not allowed */
- SERVER_ERROR, /* server error */
- TIMED_OUT, /* client timed out */
- LOST_SIGNAL,
- LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */
- INCOMING_REJECTED, /* an incoming call that was rejected */
- POWER_OFF, /* radio is turned off explicitly */
- OUT_OF_SERVICE, /* out of service */
- ICC_ERROR, /* No ICC, ICC locked, or other ICC error */
- CALL_BARRED, /* call was blocked by call barring */
- FDN_BLOCKED, /* call was blocked by fixed dial number */
- CS_RESTRICTED, /* call was blocked by restricted all voice access */
- CS_RESTRICTED_NORMAL, /* call was blocked by restricted normal voice access */
- CS_RESTRICTED_EMERGENCY, /* call was blocked by restricted emergency voice access */
- UNOBTAINABLE_NUMBER, /* Unassigned number (3GPP TS 24.008 table 10.5.123) */
- CDMA_LOCKED_UNTIL_POWER_CYCLE, /* MS is locked until next power cycle */
- CDMA_DROP,
- CDMA_INTERCEPT, /* INTERCEPT order received, MS state idle entered */
- CDMA_REORDER, /* MS has been redirected, call is cancelled */
- CDMA_SO_REJECT, /* service option rejection */
- CDMA_RETRY_ORDER, /* requested service is rejected, retry delay is set */
- CDMA_ACCESS_FAILURE,
- CDMA_PREEMPTED,
- CDMA_NOT_EMERGENCY, /* not an emergency call */
- CDMA_ACCESS_BLOCKED, /* Access Blocked by CDMA network */
- ERROR_UNSPECIFIED,
-
- UNKNOWN /* Disconnect cause doesn't map to any above */
- }
-
private static final Map<Integer, String> STATE_MAP = ImmutableMap.<Integer, String>builder()
.put(Call.State.ACTIVE, "ACTIVE")
.put(Call.State.CALL_WAITING, "CALL_WAITING")
@@ -176,7 +127,8 @@
private int mState = State.INVALID;
// Reason for disconnect. Valid when the call state is DISCONNECTED.
- private DisconnectCause mDisconnectCause = DisconnectCause.UNKNOWN;
+ // Valid values are defined in {@link DisconnectCause}.
+ private int mDisconnectCause = DisconnectCause.NOT_VALID;
// Bit mask of capabilities unique to this call.
private int mCapabilities;
@@ -258,7 +210,8 @@
mIdentification.setCnapName(cnapName);
}
- public DisconnectCause getDisconnectCause() {
+ /** Returns call disconnect cause; values are defined in {@link DisconnectCause}. */
+ public int getDisconnectCause() {
if (mState == State.DISCONNECTED || mState == State.IDLE) {
return mDisconnectCause;
}
@@ -266,7 +219,8 @@
return DisconnectCause.NOT_DISCONNECTED;
}
- public void setDisconnectCause(DisconnectCause cause) {
+ /** Sets the call disconnect cause; values are defined in {@link DisconnectCause}. */
+ public void setDisconnectCause(int cause) {
mDisconnectCause = cause;
}
@@ -338,7 +292,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mCallId);
dest.writeInt(mState);
- dest.writeString(getDisconnectCause().toString());
+ dest.writeInt(getDisconnectCause());
dest.writeInt(getCapabilities());
dest.writeLong(getConnectTime());
dest.writeIntArray(Ints.toArray(mChildCallIds));
@@ -353,7 +307,7 @@
private Call(Parcel in) {
mCallId = in.readInt();
mState = in.readInt();
- mDisconnectCause = DisconnectCause.valueOf(in.readString());
+ mDisconnectCause = in.readInt();
mCapabilities = in.readInt();
mConnectTime = in.readLong();
mChildCallIds.addAll(Ints.asList(in.createIntArray()));
diff --git a/res/drawable-hdpi/ic_back_arrow.png b/res/drawable-hdpi/ic_back_arrow.png
new file mode 100644
index 0000000..aad4f36
--- /dev/null
+++ b/res/drawable-hdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_overflow_lt.png b/res/drawable-hdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..2561b8c
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_back_arrow.png b/res/drawable-mdpi/ic_back_arrow.png
new file mode 100644
index 0000000..56eb887
--- /dev/null
+++ b/res/drawable-mdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_overflow_lt.png b/res/drawable-mdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..7dc68c9
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_back_arrow.png b/res/drawable-xhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..9d46e3d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_overflow_lt.png b/res/drawable-xhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..95e436c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_back_arrow.png b/res/drawable-xxhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..66b6e35
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_overflow_lt.png b/res/drawable-xxhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 0000000..b9f0c3d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/res/drawable/actionbar_background.xml b/res/drawable/actionbar_background.xml
new file mode 100644
index 0000000..eabceac
--- /dev/null
+++ b/res/drawable/actionbar_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/actionbar_underline" />
+ </shape>
+ </item>
+ <item android:bottom="2dp">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/actionbar_background_color" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f0a3e9f..12b3a0b 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -36,5 +36,12 @@
<!-- Settings screen should use the same colors as the Dialer -->
<color name="phone_settings_background_color">#f5f5f5</color>
- <color name="phone_settings_actionbar_color">#e6e6e6</color>
+ <!-- Action bar text color. Ensure this stays in sync with Dialer actionbar_text_color. -->
+ <color name="phone_settings_actionbar_text_color">#FFFFFF</color>
+ <!-- Background color of action bars. Ensure this stays in sync with Dialer
+ actionbar_background_color. -->
+ <color name="actionbar_background_color">#3B77E7</color>
+ <!-- Underline color of action bars. Ensure this stays in sync with Dialer
+ actionbar_underline. -->
+ <color name="actionbar_underline">#3265C1</color>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8e92006..47d066b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -379,6 +379,10 @@
<string name="roaming_reenable_message">You\'ve lost data connectivity because you left your home network with data roaming turned off.</string>
<!-- Mobile network settings screen, dialog message when user selects the Data roaming check box -->
<string name="roaming_warning">Allow data roaming? You may incur significant roaming charges!</string>
+
+ <!-- USSD aggregation dialog box: separator strings between messages (new-lines will be added before and after) -->
+ <string name="ussd_dialog_sep" translatable="false">----------</string>
+
<string name="gsm_umts_options">GSM/UMTS Options</string>
<string name="cdma_options">CDMA Options</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 59edb80..c3ad5b8 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -163,6 +163,8 @@
<style name="SettingsLight" parent="@android:style/Theme.Holo.Light">
<item name="android:windowBackground">@color/phone_settings_background_color</item>
<item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
</style>
<style name="Empty" parent="@android:style/Theme.Holo">
@@ -225,13 +227,27 @@
<item name="android:backgroundStacked">@color/people_app_theme_color</item>
</style>
- <style name="DialtactsActionBarStyle"
- parent="@android:style/Widget.Holo.Light.ActionBar">
- <item name="android:background">@color/phone_settings_actionbar_color</item>
+ <!-- Style for the call settings action bar. Should be kept in sync with Dialer. -->
+ <style name="DialtactsActionBarStyle" parent="android:Widget.Holo.ActionBar">
+ <item name="android:background">@drawable/actionbar_background</item>
+ <item name="android:backgroundStacked">#ffffff</item>
+ <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
<!-- Empty icon -->
<item name="android:icon">@android:color/transparent</item>
</style>
+ <!-- Text in the action bar at the top of the screen. Should be kept in sync with Dialer. -->
+ <style name="DialtactsActionBarTitleText"
+ parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/phone_settings_actionbar_text_color</item>
+ </style>
+
+ <!-- Action bar overflow menu icon. -->
+ <style name="DialtactsActionBarOverflow"
+ parent="@android:style/Widget.Holo.ActionButton.Overflow">
+ <item name="android:src">@drawable/ic_menu_overflow_lt</item>
+ </style>
+
<style name="SimImportTheme"
parent="@android:style/Theme.Holo.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
diff --git a/src/com/android/phone/BluetoothPhoneService.java b/src/com/android/phone/BluetoothPhoneService.java
index cd9c696..a2e1288 100644
--- a/src/com/android/phone/BluetoothPhoneService.java
+++ b/src/com/android/phone/BluetoothPhoneService.java
@@ -42,6 +42,8 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.CallManager;
+import com.android.phone.CallGatewayManager.RawGatewayInfo;
+
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -60,6 +62,7 @@
private BluetoothAdapter mAdapter;
private CallManager mCM;
+ private CallGatewayManager mCallGatewayManager;
private BluetoothHeadset mBluetoothHeadset;
@@ -104,6 +107,7 @@
if (VDBG) Log.d(TAG, "mAdapter null");
return;
}
+ mCallGatewayManager = CallGatewayManager.getInstance();
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -332,7 +336,9 @@
}
// end the result
// when index is 0, other parameter does not matter
- mBluetoothHeadset.clccResponse(0, 0, 0, 0, false, "", 0);
+ if (mBluetoothHeadset != null) {
+ mBluetoothHeadset.clccResponse(0, 0, 0, 0, false, "", 0);
+ }
}
private void handleQueryPhoneState() {
@@ -519,15 +525,27 @@
mpty = call.isMultiparty();
}
- int direction = connection.isIncoming() ? 1 : 0;
+ boolean isIncoming = connection.isIncoming();
+ // For GV outgoing calls send the contact phone #, not the gateway #.
String number = connection.getAddress();
+ if (!isIncoming) {
+ RawGatewayInfo rawInfo = mCallGatewayManager.getGatewayInfo(connection);
+ if (!rawInfo.isEmpty()) {
+ number = rawInfo.trueNumber;
+ }
+ }
int type = -1;
if (number != null) {
type = PhoneNumberUtils.toaFromString(number);
+ } else {
+ number = "";
}
- mBluetoothHeadset.clccResponse(index + 1, direction, state, 0, mpty, number, type);
+ if (mBluetoothHeadset != null) {
+ mBluetoothHeadset.clccResponse(index + 1, isIncoming ? 1 : 0,
+ state, 0, mpty, number, type);
+ }
}
/** Build the +CLCC result for CDMA
@@ -650,9 +668,16 @@
// as per Bluetooth SIG PTS
}
- int direction = connection.isIncoming() ? 1 : 0;
+ boolean isIncoming = connection.isIncoming();
+ // For GV outgoing calls send the contact phone #, not the gateway #.
String number = connection.getAddress();
+ if (!isIncoming) {
+ RawGatewayInfo rawInfo = mCallGatewayManager.getGatewayInfo(connection);
+ if (!rawInfo.isEmpty()) {
+ number = rawInfo.trueNumber;
+ }
+ }
int type = -1;
if (number != null) {
type = PhoneNumberUtils.toaFromString(number);
@@ -660,7 +685,10 @@
number = "";
}
- mBluetoothHeadset.clccResponse(index + 1, direction, state, 0, mpty, number, type);
+ if (mBluetoothHeadset != null) {
+ mBluetoothHeadset.clccResponse(index + 1, isIncoming ? 1 : 0,
+ state, 0, mpty, number, type);
+ }
}
private void handleCdmaSwapSecondCallState() {
@@ -730,6 +758,10 @@
}
return true;
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ if (ringingCall.isRinging() && (mNumHeld > 0 && mNumActive == 0)) {
+ if (VDBG) log("CHLD:1 Answer the Call");
+ return PhoneUtils.answerCall(ringingCall);
+ }
// Hangup active call, answer held call
return PhoneUtils.answerAndEndActive(PhoneGlobals.getInstance().mCM, ringingCall);
} else {
@@ -762,7 +794,11 @@
Log.e(TAG, "CDMA fail to do hold active and accept held");
return false;
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- PhoneUtils.switchHoldingAndActive(backgroundCall);
+ if (ringingCall.isRinging() && (mNumHeld > 0 && mNumActive == 0)) {
+ PhoneUtils.answerCall(ringingCall);
+ } else {
+ PhoneUtils.switchHoldingAndActive(backgroundCall);
+ }
return true;
} else {
Log.e(TAG, "Unexpected phone type: " + phoneType);
diff --git a/src/com/android/phone/CallCommandService.java b/src/com/android/phone/CallCommandService.java
index 5238911..2076979 100644
--- a/src/com/android/phone/CallCommandService.java
+++ b/src/com/android/phone/CallCommandService.java
@@ -168,30 +168,10 @@
@Override
public void swap() {
- if (!PhoneUtils.okToSwapCalls(mCallManager)) {
- // TODO: throw an error instead?
- return;
- }
-
- // Swap the fg and bg calls.
- // In the future we may provides some way for user to choose among
- // multiple background calls, for now, always act on the first background calll.
- PhoneUtils.switchHoldingAndActive(mCallManager.getFirstActiveBgCall());
-
- final PhoneGlobals mApp = PhoneGlobals.getInstance();
-
- // If we have a valid BluetoothPhoneService then since CDMA network or
- // Telephony FW does not send us information on which caller got swapped
- // we need to update the second call active state in BluetoothPhoneService internally
- if (mCallManager.getBgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- final IBluetoothHeadsetPhone btPhone = mApp.getBluetoothPhoneService();
- if (btPhone != null) {
- try {
- btPhone.cdmaSwapSecondCallState();
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ try {
+ PhoneUtils.swap();
+ } catch (Exception e) {
+ Log.e(TAG, "Error during swap().", e);
}
}
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 5cc3aa3..035baaf 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -46,6 +46,7 @@
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
@@ -174,6 +175,8 @@
private static final String BUTTON_FDN_KEY = "button_fdn_key";
private static final String BUTTON_RESPOND_VIA_SMS_KEY = "button_respond_via_sms_key";
+ private static final String BUTTON_RINGTONE_CATEGORY = "button_ringtone_category_key";
+
private static final String BUTTON_RINGTONE_KEY = "button_ringtone_key";
private static final String BUTTON_VIBRATE_ON_RING = "button_vibrate_on_ring";
private static final String BUTTON_PLAY_DTMF_TONE = "button_play_dtmf_tone";
@@ -1539,7 +1542,8 @@
if (vibrator != null && vibrator.hasVibrator()) {
mVibrateWhenRinging.setOnPreferenceChangeListener(this);
} else {
- prefSet.removePreference(mVibrateWhenRinging);
+ PreferenceCategory ringToneCategory = (PreferenceCategory)findPreference(BUTTON_RINGTONE_CATEGORY);
+ ringToneCategory.removePreference(mVibrateWhenRinging);
mVibrateWhenRinging = null;
}
}
diff --git a/src/com/android/phone/CallGatewayManager.java b/src/com/android/phone/CallGatewayManager.java
index 6fe2444..81bee07 100644
--- a/src/com/android/phone/CallGatewayManager.java
+++ b/src/com/android/phone/CallGatewayManager.java
@@ -25,7 +25,7 @@
import com.android.internal.telephony.Connection;
import com.google.android.collect.Maps;
-import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
/**
* This class manages gateway information for outgoing calls. When calls are made, they may contain
@@ -68,9 +68,19 @@
public static final RawGatewayInfo EMPTY_INFO = new RawGatewayInfo(null, null, null);
- private final HashMap<Connection, RawGatewayInfo> mMap = Maps.newHashMap();
+ private final ConcurrentHashMap<Connection, RawGatewayInfo> mMap =
+ new ConcurrentHashMap<Connection, RawGatewayInfo>(4, 0.9f, 1);
- public CallGatewayManager() {
+ private static CallGatewayManager sSingleton;
+
+ public static synchronized CallGatewayManager getInstance() {
+ if (sSingleton == null) {
+ sSingleton = new CallGatewayManager();
+ }
+ return sSingleton;
+ }
+
+ private CallGatewayManager() {
}
/**
diff --git a/src/com/android/phone/CallLogger.java b/src/com/android/phone/CallLogger.java
index 644812f..9e76db4 100644
--- a/src/com/android/phone/CallLogger.java
+++ b/src/com/android/phone/CallLogger.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.SystemProperties;
import android.provider.CallLog.Calls;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
@@ -87,13 +88,13 @@
* Came as logCall(Connection,int) but calculates the call type from the connection object.
*/
public void logCall(Connection c) {
- final Connection.DisconnectCause cause = c.getDisconnectCause();
+ final int cause = c.getDisconnectCause();
// Set the "type" to be displayed in the call log (see constants in CallLog.Calls)
final int callLogType;
if (c.isIncoming()) {
- callLogType = (cause == Connection.DisconnectCause.INCOMING_MISSED ?
+ callLogType = (cause == DisconnectCause.INCOMING_MISSED ?
Calls.MISSED_TYPE : Calls.INCOMING_TYPE);
} else {
callLogType = Calls.OUTGOING_TYPE;
diff --git a/src/com/android/phone/CallModeler.java b/src/com/android/phone/CallModeler.java
index e4ed147..72eaed0 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -20,6 +20,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
@@ -38,7 +39,6 @@
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
@@ -555,8 +555,7 @@
changed = true;
}
- final Call.DisconnectCause newDisconnectCause =
- translateDisconnectCauseFromTelephony(connection.getDisconnectCause());
+ final int newDisconnectCause = connection.getDisconnectCause();
if (call.getDisconnectCause() != newDisconnectCause) {
call.setDisconnectCause(newDisconnectCause);
changed = true;
@@ -789,73 +788,6 @@
return retval;
}
- private final ImmutableMap<Connection.DisconnectCause, Call.DisconnectCause> CAUSE_MAP =
- ImmutableMap.<Connection.DisconnectCause, Call.DisconnectCause>builder()
- .put(Connection.DisconnectCause.BUSY, Call.DisconnectCause.BUSY)
- .put(Connection.DisconnectCause.CALL_BARRED, Call.DisconnectCause.CALL_BARRED)
- .put(Connection.DisconnectCause.CDMA_ACCESS_BLOCKED,
- Call.DisconnectCause.CDMA_ACCESS_BLOCKED)
- .put(Connection.DisconnectCause.CDMA_ACCESS_FAILURE,
- Call.DisconnectCause.CDMA_ACCESS_FAILURE)
- .put(Connection.DisconnectCause.CDMA_DROP, Call.DisconnectCause.CDMA_DROP)
- .put(Connection.DisconnectCause.CDMA_INTERCEPT, Call.DisconnectCause.CDMA_INTERCEPT)
- .put(Connection.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE,
- Call.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE)
- .put(Connection.DisconnectCause.CDMA_NOT_EMERGENCY,
- Call.DisconnectCause.CDMA_NOT_EMERGENCY)
- .put(Connection.DisconnectCause.CDMA_PREEMPTED, Call.DisconnectCause.CDMA_PREEMPTED)
- .put(Connection.DisconnectCause.CDMA_REORDER, Call.DisconnectCause.CDMA_REORDER)
- .put(Connection.DisconnectCause.CDMA_RETRY_ORDER,
- Call.DisconnectCause.CDMA_RETRY_ORDER)
- .put(Connection.DisconnectCause.CDMA_SO_REJECT, Call.DisconnectCause.CDMA_SO_REJECT)
- .put(Connection.DisconnectCause.CONGESTION, Call.DisconnectCause.CONGESTION)
- .put(Connection.DisconnectCause.CS_RESTRICTED, Call.DisconnectCause.CS_RESTRICTED)
- .put(Connection.DisconnectCause.CS_RESTRICTED_EMERGENCY,
- Call.DisconnectCause.CS_RESTRICTED_EMERGENCY)
- .put(Connection.DisconnectCause.CS_RESTRICTED_NORMAL,
- Call.DisconnectCause.CS_RESTRICTED_NORMAL)
- .put(Connection.DisconnectCause.ERROR_UNSPECIFIED,
- Call.DisconnectCause.ERROR_UNSPECIFIED)
- .put(Connection.DisconnectCause.FDN_BLOCKED, Call.DisconnectCause.FDN_BLOCKED)
- .put(Connection.DisconnectCause.ICC_ERROR, Call.DisconnectCause.ICC_ERROR)
- .put(Connection.DisconnectCause.INCOMING_MISSED,
- Call.DisconnectCause.INCOMING_MISSED)
- .put(Connection.DisconnectCause.INCOMING_REJECTED,
- Call.DisconnectCause.INCOMING_REJECTED)
- .put(Connection.DisconnectCause.INVALID_CREDENTIALS,
- Call.DisconnectCause.INVALID_CREDENTIALS)
- .put(Connection.DisconnectCause.INVALID_NUMBER,
- Call.DisconnectCause.INVALID_NUMBER)
- .put(Connection.DisconnectCause.LIMIT_EXCEEDED, Call.DisconnectCause.LIMIT_EXCEEDED)
- .put(Connection.DisconnectCause.LOCAL, Call.DisconnectCause.LOCAL)
- .put(Connection.DisconnectCause.LOST_SIGNAL, Call.DisconnectCause.LOST_SIGNAL)
- .put(Connection.DisconnectCause.MMI, Call.DisconnectCause.MMI)
- .put(Connection.DisconnectCause.NORMAL, Call.DisconnectCause.NORMAL)
- .put(Connection.DisconnectCause.NOT_DISCONNECTED,
- Call.DisconnectCause.NOT_DISCONNECTED)
- .put(Connection.DisconnectCause.NUMBER_UNREACHABLE,
- Call.DisconnectCause.NUMBER_UNREACHABLE)
- .put(Connection.DisconnectCause.OUT_OF_NETWORK, Call.DisconnectCause.OUT_OF_NETWORK)
- .put(Connection.DisconnectCause.OUT_OF_SERVICE, Call.DisconnectCause.OUT_OF_SERVICE)
- .put(Connection.DisconnectCause.POWER_OFF, Call.DisconnectCause.POWER_OFF)
- .put(Connection.DisconnectCause.SERVER_ERROR, Call.DisconnectCause.SERVER_ERROR)
- .put(Connection.DisconnectCause.SERVER_UNREACHABLE,
- Call.DisconnectCause.SERVER_UNREACHABLE)
- .put(Connection.DisconnectCause.TIMED_OUT, Call.DisconnectCause.TIMED_OUT)
- .put(Connection.DisconnectCause.UNOBTAINABLE_NUMBER,
- Call.DisconnectCause.UNOBTAINABLE_NUMBER)
- .build();
-
- private Call.DisconnectCause translateDisconnectCauseFromTelephony(
- Connection.DisconnectCause causeSource) {
-
- if (CAUSE_MAP.containsKey(causeSource)) {
- return CAUSE_MAP.get(causeSource);
- }
-
- return Call.DisconnectCause.UNKNOWN;
- }
-
/**
* Gets an existing callId for a connection, or creates one if none exists.
* This function does NOT set any of the Connection data onto the Call class.
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 80807a2..aa4270e 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -46,6 +46,7 @@
import android.os.Vibrator;
import android.provider.CallLog.Calls;
import android.provider.Settings;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
@@ -899,7 +900,7 @@
mVoicePrivacyState = false;
Connection c = (Connection) r.result;
if (c != null) {
- log("onDisconnect: cause = " + c.getDisconnectCause()
+ log("onDisconnect: cause = " + DisconnectCause.toString(c.getDisconnectCause())
+ ", incoming = " + c.isIncoming()
+ ", date = " + c.getCreateTime());
} else {
@@ -974,34 +975,34 @@
// The "Busy" or "Congestion" tone is the highest priority:
if (c != null) {
- Connection.DisconnectCause cause = c.getDisconnectCause();
- if (cause == Connection.DisconnectCause.BUSY) {
+ int cause = c.getDisconnectCause();
+ if (cause == DisconnectCause.BUSY) {
if (DBG) log("- need to play BUSY tone!");
toneToPlay = InCallTonePlayer.TONE_BUSY;
- } else if (cause == Connection.DisconnectCause.CONGESTION) {
+ } else if (cause == DisconnectCause.CONGESTION) {
if (DBG) log("- need to play CONGESTION tone!");
toneToPlay = InCallTonePlayer.TONE_CONGESTION;
- } else if (((cause == Connection.DisconnectCause.NORMAL)
- || (cause == Connection.DisconnectCause.LOCAL))
+ } else if (((cause == DisconnectCause.NORMAL)
+ || (cause == DisconnectCause.LOCAL))
&& (mApplication.isOtaCallInActiveState())) {
if (DBG) log("- need to play OTA_CALL_END tone!");
toneToPlay = InCallTonePlayer.TONE_OTA_CALL_END;
- } else if (cause == Connection.DisconnectCause.CDMA_REORDER) {
+ } else if (cause == DisconnectCause.CDMA_REORDER) {
if (DBG) log("- need to play CDMA_REORDER tone!");
toneToPlay = InCallTonePlayer.TONE_REORDER;
- } else if (cause == Connection.DisconnectCause.CDMA_INTERCEPT) {
+ } else if (cause == DisconnectCause.CDMA_INTERCEPT) {
if (DBG) log("- need to play CDMA_INTERCEPT tone!");
toneToPlay = InCallTonePlayer.TONE_INTERCEPT;
- } else if (cause == Connection.DisconnectCause.CDMA_DROP) {
+ } else if (cause == DisconnectCause.CDMA_DROP) {
if (DBG) log("- need to play CDMA_DROP tone!");
toneToPlay = InCallTonePlayer.TONE_CDMA_DROP;
- } else if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) {
+ } else if (cause == DisconnectCause.OUT_OF_SERVICE) {
if (DBG) log("- need to play OUT OF SERVICE tone!");
toneToPlay = InCallTonePlayer.TONE_OUT_OF_SERVICE;
- } else if (cause == Connection.DisconnectCause.UNOBTAINABLE_NUMBER) {
+ } else if (cause == DisconnectCause.UNOBTAINABLE_NUMBER) {
if (DBG) log("- need to play TONE_UNOBTAINABLE_NUMBER tone!");
toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER;
- } else if (cause == Connection.DisconnectCause.ERROR_UNSPECIFIED) {
+ } else if (cause == DisconnectCause.ERROR_UNSPECIFIED) {
if (DBG) log("- DisconnectCause is ERROR_UNSPECIFIED: play TONE_CALL_ENDED!");
toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
}
@@ -1017,9 +1018,9 @@
if ((toneToPlay == InCallTonePlayer.TONE_NONE)
&& (mCM.getState() == PhoneConstants.State.IDLE)
&& (c != null)) {
- Connection.DisconnectCause cause = c.getDisconnectCause();
- if ((cause == Connection.DisconnectCause.NORMAL) // remote hangup
- || (cause == Connection.DisconnectCause.LOCAL)) { // local hangup
+ int cause = c.getDisconnectCause();
+ if ((cause == DisconnectCause.NORMAL) // remote hangup
+ || (cause == DisconnectCause.LOCAL)) { // local hangup
if (VDBG) log("- need to play CALL_ENDED tone!");
toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
mIsCdmaRedialCall = false;
@@ -1055,9 +1056,9 @@
}
final long date = c.getCreateTime();
- final Connection.DisconnectCause cause = c.getDisconnectCause();
+ final int cause = c.getDisconnectCause();
final boolean missedCall = c.isIncoming() &&
- (cause == Connection.DisconnectCause.INCOMING_MISSED);
+ (cause == DisconnectCause.INCOMING_MISSED);
if (missedCall) {
// Show the "Missed call" notification.
// (Note we *don't* do this if this was an incoming call that
@@ -1086,10 +1087,10 @@
if (((mPreviousCdmaCallState == Call.State.DIALING)
|| (mPreviousCdmaCallState == Call.State.ALERTING))
&& (!isEmergencyNumber)
- && (cause != Connection.DisconnectCause.INCOMING_MISSED )
- && (cause != Connection.DisconnectCause.NORMAL)
- && (cause != Connection.DisconnectCause.LOCAL)
- && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
+ && (cause != DisconnectCause.INCOMING_MISSED )
+ && (cause != DisconnectCause.NORMAL)
+ && (cause != DisconnectCause.LOCAL)
+ && (cause != DisconnectCause.INCOMING_REJECTED)) {
if (!mIsCdmaRedialCall) {
if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) {
// TODO: (Moto): The contact reference data may need to be stored and use
diff --git a/src/com/android/phone/EmergencyCallHelper.java b/src/com/android/phone/EmergencyCallHelper.java
index 47f0e54..74ce088 100644
--- a/src/com/android/phone/EmergencyCallHelper.java
+++ b/src/com/android/phone/EmergencyCallHelper.java
@@ -29,6 +29,7 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
import android.util.Log;
@@ -234,11 +235,12 @@
*/
private void onDisconnect(Message msg) {
Connection conn = (Connection) ((AsyncResult) msg.obj).result;
- Connection.DisconnectCause cause = conn.getDisconnectCause();
+ int cause = conn.getDisconnectCause();
if (DBG) log("onDisconnect: connection '" + conn
- + "', addr '" + conn.getAddress() + "', cause = " + cause);
+ + "', addr '" + conn.getAddress()
+ + "', cause = " + DisconnectCause.toString(cause));
- if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) {
+ if (cause == DisconnectCause.OUT_OF_SERVICE) {
// Wait a bit more and try again (or just bail out totally if
// we've had too many failures.)
if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry...");
diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java
index 6e0c13e..1267286 100644
--- a/src/com/android/phone/OutgoingCallBroadcaster.java
+++ b/src/com/android/phone/OutgoingCallBroadcaster.java
@@ -46,30 +46,35 @@
import com.android.internal.telephony.TelephonyCapabilities;
/**
- * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and
- * broadcasts the ACTION_NEW_OUTGOING_CALL intent which allows other
- * applications to monitor, redirect, or prevent the outgoing call.
-
+ * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the
+ * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which
+ * contains the phone number being dialed. Applications can use this intent to (1) see which numbers
+ * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call
+ * from being placed.
+ *
* After the other applications have had a chance to see the
* ACTION_NEW_OUTGOING_CALL intent, it finally reaches the
* {@link OutgoingCallReceiver}, which passes the (possibly modified)
* intent on to the {@link SipCallOptionHandler}, which will
* ultimately start the call using the CallController.placeCall() API.
*
- * Emergency calls and calls where no number is present (like for a CDMA
- * "empty flash" or a nonexistent voicemail number) are exempt from being
- * broadcast.
+ * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail
+ * number) are exempt from being broadcast.
+ * Calls to emergency numbers are still broadcast for informative purposes. The call is placed
+ * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
*/
public class OutgoingCallBroadcaster extends Activity
implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
- private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS;
private static final String TAG = "OutgoingCallBroadcaster";
private static final boolean DBG =
(PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
// Do not check in with VDBG = true, since that may write PII to the system log.
private static final boolean VDBG = false;
+ /** Required permission for any app that wants to consume ACTION_NEW_OUTGOING_CALL. */
+ private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS;
+
public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE";
public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED";
public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI";
@@ -459,7 +464,7 @@
launchedFromUid = -1;
launchedFromPackage = null;
}
- if (appOps.noteOp(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
+ if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
!= AppOpsManager.MODE_ALLOWED) {
Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package "
+ launchedFromPackage);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 3f35900..62d542f 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -447,7 +447,7 @@
CallLogger callLogger = new CallLogger(this, new CallLogAsync());
- callGatewayManager = new CallGatewayManager();
+ callGatewayManager = CallGatewayManager.getInstance();
// Create the CallController singleton, which is the interface
// to the telephony layer for user-initiated telephony functionality
@@ -489,7 +489,8 @@
callHandlerServiceProxy = new CallHandlerServiceProxy(this, callModeler,
callCommandService, audioRouter);
- phoneMgr = PhoneInterfaceManager.init(this, phone, callHandlerServiceProxy);
+ phoneMgr = PhoneInterfaceManager.init(this, phone, callHandlerServiceProxy, callModeler,
+ dtmfTonePlayer);
// Create the CallNotifer singleton, which handles
// asynchronous events from the telephony layer (like
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 82b5e9f..cf86e7a 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.bluetooth.IBluetoothHeadsetPhone;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -27,9 +28,11 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.telephony.NeighboringCellInfo;
@@ -38,21 +41,29 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.ITelephonyListener;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.PhoneConstants;
+import com.android.services.telephony.common.Call;
-import java.util.List;
+import com.android.internal.util.HexDump;
+
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
/**
* Implementation of the ITelephony interface.
*/
-public class PhoneInterfaceManager extends ITelephony.Stub {
+public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
private static final String LOG_TAG = "PhoneInterfaceManager";
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
private static final boolean DBG_LOC = false;
@@ -74,6 +85,14 @@
AppOpsManager mAppOps;
MainThreadHandler mMainThreadHandler;
CallHandlerServiceProxy mCallHandlerService;
+ CallModeler mCallModeler;
+ DTMFTonePlayer mDtmfTonePlayer;
+ Handler mDtmfStopHandler = new Handler();
+ Runnable mDtmfStopRunnable;
+
+ private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
+ private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
+ new HashMap<IBinder, TelephonyListenerDeathRecipient>();
/**
* A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
@@ -221,10 +240,12 @@
* This is only done once, at startup, from PhoneApp.onCreate().
*/
/* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
- CallHandlerServiceProxy callHandlerService) {
+ CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
+ DTMFTonePlayer dtmfTonePlayer) {
synchronized (PhoneInterfaceManager.class) {
if (sInstance == null) {
- sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
+ sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
+ dtmfTonePlayer);
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
@@ -234,13 +255,17 @@
/** Private constructor; @see init() */
private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
- CallHandlerServiceProxy callHandlerService) {
+ CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
+ DTMFTonePlayer dtmfTonePlayer) {
mApp = app;
mPhone = phone;
mCM = PhoneGlobals.getInstance().mCM;
mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
mMainThreadHandler = new MainThreadHandler();
mCallHandlerService = callHandlerService;
+ mCallModeler = callModeler;
+ mCallModeler.addListener(this);
+ mDtmfTonePlayer = dtmfTonePlayer;
publish();
}
@@ -793,6 +818,15 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
}
+ /**
+ * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforcePrivilegedPhoneStatePermission() {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ null);
+ }
private String createTelUrl(String number) {
if (TextUtils.isEmpty(number)) {
@@ -897,4 +931,248 @@
public int getLteOnCdmaMode() {
return mPhone.getLteOnCdmaMode();
}
+
+ @Override
+ public void toggleHold() {
+ enforceModifyPermission();
+
+ try {
+ PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error during toggleHold().", e);
+ }
+ }
+
+ @Override
+ public void merge() {
+ enforceModifyPermission();
+
+ try {
+ if (PhoneUtils.okToMergeCalls(mCM)) {
+ PhoneUtils.mergeCalls(mCM);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error during merge().", e);
+ }
+ }
+
+ @Override
+ public void swap() {
+ enforceModifyPermission();
+
+ try {
+ PhoneUtils.swap();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error during swap().", e);
+ }
+ }
+
+ @Override
+ public void mute(boolean onOff) {
+ enforceModifyPermission();
+
+ try {
+ PhoneUtils.setMute(onOff);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error during mute().", e);
+ }
+ }
+
+ @Override
+ public void playDtmfTone(char digit, boolean timedShortTone) {
+ enforceModifyPermission();
+
+ synchronized (mDtmfStopHandler) {
+ try {
+ mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error playing DTMF tone.", e);
+ }
+
+ if (mDtmfStopRunnable != null) {
+ mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
+ }
+ mDtmfStopRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mDtmfStopHandler) {
+ if (mDtmfStopRunnable == this) {
+ mDtmfTonePlayer.stopDtmfTone();
+ mDtmfStopRunnable = null;
+ }
+ }
+ }
+ };
+ mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
+ }
+ }
+
+ @Override
+ public void stopDtmfTone() {
+ enforceModifyPermission();
+
+ synchronized (mDtmfStopHandler) {
+ try {
+ mDtmfTonePlayer.stopDtmfTone();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
+ }
+
+ if (mDtmfStopRunnable != null) {
+ mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
+ mDtmfStopRunnable = null;
+ }
+ }
+ }
+
+ @Override
+ public void addListener(ITelephonyListener listener) {
+ enforcePrivilegedPhoneStatePermission();
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null.");
+ }
+
+ synchronized (mListeners) {
+ IBinder listenerBinder = listener.asBinder();
+ for (ITelephonyListener l : mListeners) {
+ if (l.asBinder().equals(listenerBinder)) {
+ Log.w(LOG_TAG, "Listener already registered. Ignoring.");
+ return;
+ }
+ }
+ mListeners.add(listener);
+ mDeathRecipients.put(listener.asBinder(),
+ new TelephonyListenerDeathRecipient(listener.asBinder()));
+
+ // update the new listener so they get the full call state immediately
+ for (Call call : mCallModeler.getFullList()) {
+ try {
+ notifyListenerOfCallLocked(call, listener);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
+ removeListenerInternal(listener);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void removeListener(ITelephonyListener listener) {
+ enforcePrivilegedPhoneStatePermission();
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null.");
+ }
+
+ removeListenerInternal(listener);
+ }
+
+ private void removeListenerInternal(ITelephonyListener listener) {
+ IBinder listenerBinder = listener.asBinder();
+
+ synchronized (mListeners) {
+ for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
+ ITelephonyListener nextListener = it.next();
+ if (nextListener.asBinder().equals(listenerBinder)) {
+ TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
+ if (dr != null) {
+ dr.unlinkDeathRecipient();
+ }
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /** CallModeler.Listener implementation **/
+
+ @Override
+ public void onDisconnect(Call call) {
+ notifyListenersOfCall(call);
+ }
+
+ @Override
+ public void onIncoming(Call call) {
+ notifyListenersOfCall(call);
+ }
+
+ @Override
+ public void onUpdate(List<Call> calls) {
+ for (Call call : calls) {
+ notifyListenersOfCall(call);
+ }
+ }
+
+ @Override
+ public void onPostDialAction(
+ Connection.PostDialState state, int callId, String remainingChars, char c) { }
+
+ private void notifyListenersOfCall(Call call) {
+ synchronized (mListeners) {
+ for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
+ ITelephonyListener listener = it.next();
+ try {
+ notifyListenerOfCallLocked(call, listener);
+ } catch (RemoteException e) {
+ TelephonyListenerDeathRecipient deathRecipient =
+ mDeathRecipients.get(listener.asBinder());
+ if (deathRecipient != null) {
+ deathRecipient.unlinkDeathRecipient();
+ }
+ it.remove();
+ }
+ }
+ }
+ }
+
+ private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
+ throws RemoteException {
+ if (Binder.isProxy(listener)) {
+ listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
+ } else {
+ mMainThreadHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
+ } catch (RemoteException e) {
+ Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
+ }
+ }
+ });
+ }
+
+ }
+
+ private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
+ private final IBinder mBinder;
+
+ public TelephonyListenerDeathRecipient(IBinder listener) {
+ mBinder = listener;
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ unlinkDeathRecipient();
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mListeners) {
+ if (mListeners.contains(mBinder)) {
+ mListeners.remove(mBinder);
+ Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
+ } else {
+ Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
+ "is not registered.");
+ }
+ }
+ }
+
+ public void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 2d44977..74a29e4 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -136,6 +136,10 @@
}
}
+ /** USSD information used to aggregate all USSD messages */
+ private static AlertDialog sUssdDialog = null;
+ private static StringBuilder sUssdMsg = new StringBuilder();
+
/**
* Handler that tracks the connections and updates the value of the
* Mute settings for each connection as needed.
@@ -720,11 +724,6 @@
// we dialed an MMI (see below).
}
- // Now that the call is successful, we can save the gateway info for the call
- if (callGateway != null) {
- callGateway.setGatewayInfoForConnection(connection, gatewayInfo);
- }
-
int phoneType = phone.getPhoneType();
// On GSM phones, null is returned for MMI codes
@@ -736,6 +735,11 @@
status = CALL_STATUS_FAILED;
}
} else {
+ // Now that the call is successful, we can save the gateway info for the call
+ if (callGateway != null) {
+ callGateway.setGatewayInfoForConnection(connection, gatewayInfo);
+ }
+
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
updateCdmaCallStateOnNewOutgoingCall(app, connection);
}
@@ -833,6 +837,33 @@
}
}
+ static void swap() {
+ final PhoneGlobals mApp = PhoneGlobals.getInstance();
+ if (!okToSwapCalls(mApp.mCM)) {
+ // TODO: throw an error instead?
+ return;
+ }
+
+ // Swap the fg and bg calls.
+ // In the future we may provide some way for user to choose among
+ // multiple background calls, for now, always act on the first background call.
+ PhoneUtils.switchHoldingAndActive(mApp.mCM.getFirstActiveBgCall());
+
+ // If we have a valid BluetoothPhoneService then since CDMA network or
+ // Telephony FW does not send us information on which caller got swapped
+ // we need to update the second call active state in BluetoothPhoneService internally
+ if (mApp.mCM.getBgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ final IBluetoothHeadsetPhone btPhone = mApp.getBluetoothPhoneService();
+ if (btPhone != null) {
+ try {
+ btPhone.cdmaSwapSecondCallState();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ }
+ }
+
/**
* @param heldCall is the background call want to be swapped
*/
@@ -1100,18 +1131,33 @@
// displaying system alert dialog on the screen instead of
// using another activity to display the message. This
// places the message at the forefront of the UI.
- AlertDialog newDialog = new AlertDialog.Builder(context)
- .setMessage(text)
- .setPositiveButton(R.string.ok, null)
- .setCancelable(true)
- .create();
- newDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- newDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ if (sUssdDialog == null) {
+ sUssdDialog = new AlertDialog.Builder(context)
+ .setPositiveButton(R.string.ok, null)
+ .setCancelable(true)
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ sUssdMsg.setLength(0);
+ }
+ })
+ .create();
- newDialog.show();
+ sUssdDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ sUssdDialog.getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ }
+ if (sUssdMsg.length() != 0) {
+ sUssdMsg
+ .insert(0, "\n")
+ .insert(0, app.getResources().getString(R.string.ussd_dialog_sep))
+ .insert(0, "\n");
+ }
+ sUssdMsg.insert(0, text);
+ sUssdDialog.setMessage(sUssdMsg.toString());
+ sUssdDialog.show();
} else {
if (DBG) log("USSD code has requested user input. Constructing input dialog.");
@@ -1911,7 +1957,7 @@
// in use.
app.updateWakeState();
- app.mCM.setEchoSuppressionEnabled(flag);
+ app.mCM.setEchoSuppressionEnabled();
}
/**