Merge "Revert "Revert "Add color to PhoneAccount objects (2/3)""" into lmp-sprout-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3877edf..5d11fb3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.BIND_CONNECTION_SERVICE" />
<uses-permission android:name="android.permission.BIND_INCALL_SERVICE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.BROADCAST_CALLLOG_INFO" />
<!-- Protects the ability to register any PhoneAccount with a capability flags of either
PhoneAccount#CAPABILITY_CALL_PROVIDER or PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. -->
@@ -44,6 +45,16 @@
android:label="Register CALL_PROVIDER or SIM_SUBSCRIPTION PhoneAccount"
android:protectionLevel="signature"/>
+ <permission
+ android:name="android.permission.BROADCAST_CALLLOG_INFO"
+ android:label="Broadcast the call type/duration information"
+ android:protectionLevel="signature|system"/>
+
+ <permission
+ android:name="android.permission.PROCESS_CALLLOG_INFO"
+ android:label="Register to handle the broadcasted call type/duration information"
+ android:protectionLevel="signature|system"/>
+
<!-- Declare which SDK level this application was built against. This is needed so that IDEs
can check for incompatible APIs. -->
<uses-sdk android:minSdkVersion="19" />
diff --git a/src/com/android/server/telecom/BluetoothPhoneService.java b/src/com/android/server/telecom/BluetoothPhoneService.java
index 43be93a..51dbbdf 100644
--- a/src/com/android/server/telecom/BluetoothPhoneService.java
+++ b/src/com/android/server/telecom/BluetoothPhoneService.java
@@ -103,6 +103,7 @@
private int mBluetoothCallState = CALL_STATE_IDLE;
private String mRingingAddress = null;
private int mRingingAddressType = 0;
+ private Call mOldHeldCall = null;
/**
* Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
@@ -643,7 +644,7 @@
}
int numActiveCalls = activeCall == null ? 0 : 1;
- int numHeldCalls = heldCall == null ? 0 : 1;
+ int numHeldCalls = callsManager.getNumHeldCalls();
// For conference calls which support swapping the active call within the conference
// (namely CDMA calls) we need to expose that as a held call in order for the BT device
@@ -664,6 +665,7 @@
bluetoothCallState != mBluetoothCallState ||
!TextUtils.equals(ringingAddress, mRingingAddress) ||
ringingAddressType != mRingingAddressType ||
+ heldCall != mOldHeldCall ||
force)) {
// If the call is transitioning into the alerting state, send DIALING first.
@@ -672,6 +674,7 @@
boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
bluetoothCallState == CALL_STATE_ALERTING;
+ mOldHeldCall = heldCall;
mNumActiveCalls = numActiveCalls;
mNumHeldCalls = numHeldCalls;
mBluetoothCallState = bluetoothCallState;
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 5e66850..a0a8a62 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -557,8 +557,12 @@
}
void setCallCapabilities(int callCapabilities) {
+ setCallCapabilities(callCapabilities, false /* forceUpdate */);
+ }
+
+ void setCallCapabilities(int callCapabilities, boolean forceUpdate) {
Log.v(this, "setCallCapabilities: %s", PhoneCapabilities.toString(callCapabilities));
- if (mCallCapabilities != callCapabilities) {
+ if (forceUpdate || mCallCapabilities != callCapabilities) {
mCallCapabilities = callCapabilities;
for (Listener l : mListeners) {
l.onCallCapabilitiesChanged(this);
@@ -781,7 +785,7 @@
mCreateConnectionProcessor.abort();
} else if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT
|| mState == CallState.CONNECTING) {
- handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.LOCAL));
+ handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
} else {
Log.v(this, "Cannot abort a call which isn't either PRE_DIAL_WAIT or CONNECTING");
}
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 1e305e2..a89dcea 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -330,7 +330,7 @@
}
private void updateAudioStreamAndMode() {
- Log.v(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging,
+ Log.i(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging,
mIsTonePlaying);
if (mIsRinging) {
requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
@@ -359,7 +359,7 @@
}
private void requestAudioFocusAndSetMode(int stream, int mode) {
- Log.v(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream);
+ Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream);
Preconditions.checkState(stream != STREAM_NONE);
// Even if we already have focus, if the stream is different we update audio manager to give
@@ -392,7 +392,12 @@
Preconditions.checkState(hasFocus());
int oldMode = mAudioManager.getMode();
Log.v(this, "Request to change audio mode from %d to %d", oldMode, newMode);
+
if (oldMode != newMode) {
+ if (oldMode == AudioManager.MODE_IN_CALL && newMode == AudioManager.MODE_RINGTONE) {
+ Log.i(this, "Transition from IN_CALL -> RINGTONE. Resetting to NORMAL first.");
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ }
mAudioManager.setMode(newMode);
mMostRecentlyUsedMode = newMode;
}
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
old mode 100644
new mode 100755
index 89d9316..658af10
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -17,6 +17,8 @@
package com.android.server.telecom;
import android.content.Context;
+import android.content.Intent;
+import android.Manifest.permission;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.CallLog.Calls;
@@ -83,6 +85,12 @@
private static final String TAG = CallLogManager.class.getSimpleName();
private final Context mContext;
+ private static final String ACTION_CALLS_TABLE_ADD_ENTRY =
+ "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY";
+ private static final String PERMISSION_PROCESS_CALLLOG_INFO =
+ "android.permission.PROCESS_CALLLOG_INFO";
+ private static final String CALL_TYPE = "callType";
+ private static final String CALL_DURATION = "duration";
public CallLogManager(Context context) {
mContext = context;
@@ -174,6 +182,8 @@
// Don't log emergency numbers if the device doesn't allow it.
final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
+ sendAddCallBroadcast(callType, duration);
+
if (isOkToLogThisCall) {
Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
+ Log.pii(number) + "," + presentation + ", " + callType
@@ -293,4 +303,11 @@
}
}
}
+
+ private void sendAddCallBroadcast(int callType, long duration) {
+ Intent callAddIntent = new Intent(ACTION_CALLS_TABLE_ADD_ENTRY);
+ callAddIntent.putExtra(CALL_TYPE, callType);
+ callAddIntent.putExtra(CALL_DURATION, duration);
+ mContext.sendBroadcast(callAddIntent, PERMISSION_PROCESS_CALLLOG_INFO);
+ }
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index de53c05..e07b279 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -17,6 +17,9 @@
package com.android.server.telecom;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CallLog.Calls;
@@ -433,7 +436,10 @@
return null;
}
- if (phoneAccountHandle == null && accounts.size() > 1 && !isEmergencyCall) {
+ boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
+ !isEmergencyCall;
+
+ if (needsAccountSelection) {
// This is the state where the user is expected to select an account
call.setState(CallState.PRE_DIAL_WAIT);
extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
@@ -443,7 +449,8 @@
call.setExtras(extras);
- if (isPotentialMMICode(handle) || isPotentialInCallMMICode) {
+ // Do not add the call if it is a potential MMI code.
+ if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
call.addListener(this);
} else {
addCall(call);
@@ -692,7 +699,7 @@
mProximitySensorManager.turnOff(screenOnImmediately);
}
- void phoneAccountSelected(Call call, PhoneAccountHandle account) {
+ void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
if (!mCalls.contains(call)) {
Log.i(this, "Attempted to add account to unknown call %s", call);
} else {
@@ -711,6 +718,10 @@
} else {
call.disconnect();
}
+
+ if (setDefault) {
+ mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
+ }
}
}
@@ -868,6 +879,16 @@
return getFirstCallWithState(CallState.ON_HOLD);
}
+ int getNumHeldCalls() {
+ int count = 0;
+ for (Call call : mCalls) {
+ if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
+ count++;
+ }
+ }
+ return count;
+ }
+
Call getFirstCallWithState(int... states) {
return getFirstCallWithState(null, states);
}
@@ -992,6 +1013,14 @@
}
updateForegroundCall();
}
+
+ // Now that a call has been removed, other calls may gain new call capabilities (for
+ // example, if only one call is left, it is now add-call capable again). Trigger the
+ // recalculation of the call's current capabilities by forcing an update. (See
+ // InCallController.toParcelableCall()).
+ for (Call otherCall : mCalls) {
+ otherCall.setCallCapabilities(otherCall.getCallCapabilities(), true /* forceUpdate */);
+ }
}
/**
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 0c6e25d..fab2679 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -238,6 +238,18 @@
Log.i(this, "Emergency number detected");
mAttemptRecords.clear();
List<PhoneAccount> allAccounts = mPhoneAccountRegistrar.getAllPhoneAccounts();
+
+ if (allAccounts.isEmpty()) {
+ // If the list of phone accounts is empty at this point, it means Telephony hasn't
+ // registered any phone accounts yet. Add a fallback emergency phone account so
+ // that emergency calls can still go through. We create a new ArrayLists here just
+ // in case the implementation of PhoneAccountRegistrar ever returns an unmodifiable
+ // list.
+ allAccounts = new ArrayList<PhoneAccount>();
+ allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
+ }
+
+
// First, add SIM phone accounts which can place emergency calls.
for (PhoneAccount phoneAccount : allAccounts) {
if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index 5a93464..d4af791 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -139,7 +139,8 @@
try {
call = mCallIdMapper.getCall(args.arg1);
if (call != null) {
- mCallsManager.phoneAccountSelected(call, (PhoneAccountHandle) args.arg2);
+ mCallsManager.phoneAccountSelected(call,
+ (PhoneAccountHandle) args.arg2, args.argi1 == 1);
} else {
Log.w(this, "phoneAccountSelected, unknown call id: %s", args.arg1);
}
@@ -284,11 +285,13 @@
}
@Override
- public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle) {
+ public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle,
+ boolean setDefault) {
if (mCallIdMapper.isValidCallId(callId)) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.arg2 = accountHandle;
+ args.argi1 = setDefault? 1 : 0;
mHandler.obtainMessage(MSG_PHONE_ACCOUNT_SELECTED, args).sendToTarget();
}
}
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 2ffe599..5afc67f 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -121,7 +121,7 @@
case TONE_CALL_ENDED:
toneType = ToneGenerator.TONE_PROP_PROMPT;
toneVolume = RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 4000;
+ toneLengthMillis = 200;
break;
case TONE_OTA_CALL_ENDED:
// TODO: fill in
@@ -159,7 +159,7 @@
case TONE_REORDER:
toneType = ToneGenerator.TONE_CDMA_REORDER;
toneVolume = RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 5000;
+ toneLengthMillis = 4000;
break;
case TONE_RING_BACK:
toneType = ToneGenerator.TONE_SUP_RINGTONE;
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 8abd5b7..2c1ffb8 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -29,10 +29,13 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.UserHandle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.telecom.CallState;
import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.telephony.PhoneNumberUtils;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
@@ -166,7 +169,8 @@
configureLedOnNotification(notification);
Log.i(this, "Adding missed call notification for %s.", call);
- mNotificationManager.notify(MISSED_CALL_NOTIFICATION_ID, notification);
+ mNotificationManager.notifyAsUser(
+ null /* tag */ , MISSED_CALL_NOTIFICATION_ID, notification, UserHandle.CURRENT);
}
/** Cancels the "missed call" notification. */
@@ -276,13 +280,19 @@
try {
while (cursor.moveToNext()) {
// Get data about the missed call from the cursor
- Uri handle = Uri.parse(cursor.getString(
- cursor.getColumnIndexOrThrow(Calls.NUMBER)));
- int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
+ final String handleString = cursor.getString(
+ cursor.getColumnIndexOrThrow(Calls.NUMBER));
+ final int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
Calls.NUMBER_PRESENTATION));
- if (presentation != Calls.PRESENTATION_ALLOWED) {
+ final Uri handle;
+ if (presentation != Calls.PRESENTATION_ALLOWED
+ || TextUtils.isEmpty(handleString)) {
handle = null;
+ } else {
+ handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(handleString) ?
+ PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
+ handleString, null);
}
// Convert the data to a call object
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index ecd459a..4ee2c88 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -28,6 +28,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.PhoneNumberUtils;
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
@@ -114,6 +115,36 @@
}
/**
+ * Retrieves the phone account id for a given subscription id if it exists. Subscription ids
+ * apply only to PSTN/SIM card phone accounts so all other accounts should not have a
+ * subscription id.
+ * @param subscriptionId The subscription id for which to construct the phone account id
+ * @return The string representing the phone account id for the subscription id.
+ */
+ public String getPhoneAccountIdForSubscriptionId(long subscriptionId) {
+ return String.valueOf(subscriptionId);
+ }
+
+ /**
+ * Retrieves the subscription id for a given phone account if it exists. Subscription ids
+ * apply only to PSTN/SIM card phone accounts so all other accounts should not have a
+ * subscription id.
+ * @param accountHandle The handle for the phone account for which to retrieve the
+ * subscription id.
+ * @return The value of the subscription id (long) or -1 if it does not exist or is not valid.
+ */
+ public long getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
+ PhoneAccount account = getPhoneAccount(accountHandle);
+ if (account == null || !account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) ||
+ !TextUtils.isDigitsOnly(accountHandle.getId())) {
+ // Since no decimals or negative numbers can be valid subscription ids, only a string of
+ // numbers can be subscription id
+ return -1;
+ }
+ return Long.parseLong(accountHandle.getId());
+ }
+
+ /**
* Retrieves the default outgoing phone account supporting the specified uriScheme.
* @param uriScheme The URI scheme for the outgoing call.
* @return The {@link PhoneAccountHandle} to use.
@@ -413,6 +444,11 @@
}
}
+ public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
+ long subId = getSubscriptionIdForPhoneAccount(accountHandle);
+ return PhoneNumberUtils.isVoiceMailNumber(subId, number);
+ }
+
public void addListener(Listener l) {
mListeners.add(l);
}
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index cf65a11..5945306 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -140,12 +140,8 @@
private void onRespondedToIncomingCall(Call call) {
// Only stop the ringer if this call is the top-most incoming call.
if (getTopMostUnansweredCall() == call) {
- stopRinging();
- stopCallWaiting();
+ removeFromUnansweredCall(call);
}
-
- // We do not remove the call from mRingingCalls until the call state changes from
- // STATE_RINGING or the call is removed. see onCallStateChanged or onCallRemoved.
}
private Call getTopMostUnansweredCall() {
@@ -203,7 +199,11 @@
VIBRATION_ATTRIBUTES);
mIsVibrating = true;
}
- } else {
+ } else if (foregroundCall != null) {
+ // The first incoming call added to Telecom is not a foreground call at this point
+ // in time. If the current foreground call is null at point, don't play call-waiting
+ // as the call will eventually be promoted to the foreground call and play the
+ // ring tone.
Log.v(this, "Playing call-waiting tone.");
// All incoming calls are in background so play call waiting.
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index fbbe1c9..3af67d8 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -35,9 +35,9 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
-
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.telecom.ITelecomService;
import com.android.internal.util.IndentingPrintWriter;
@@ -327,6 +327,20 @@
}
/**
+ * @see android.telecom.TelecomManager#isVoiceMailNumber
+ */
+ @Override
+ public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
+ enforceReadPermissionOrDefaultDialer();
+ try {
+ return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
+ } catch (Exception e) {
+ Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+ throw e;
+ }
+ }
+
+ /**
* @see android.telecom.TelecomManager#silenceRinger
*/
@Override
diff --git a/src/com/android/server/telecom/TelephonyUtil.java b/src/com/android/server/telecom/TelephonyUtil.java
index 29b6f89..a130522 100644
--- a/src/com/android/server/telecom/TelephonyUtil.java
+++ b/src/com/android/server/telecom/TelephonyUtil.java
@@ -19,6 +19,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telephony.PhoneNumberUtils;
/**
@@ -33,8 +35,25 @@
private static final String PSTN_CALL_SERVICE_CLASS_NAME =
"com.android.services.telephony.TelephonyConnectionService";
+ private static final PhoneAccountHandle DEFAULT_EMERGENCY_PHONE_ACCOUNT_HANDLE =
+ new PhoneAccountHandle(
+ new ComponentName(TELEPHONY_PACKAGE_NAME, PSTN_CALL_SERVICE_CLASS_NAME), "E");
+
private TelephonyUtil() {}
+ /**
+ * @return fallback {@link PhoneAccount} to be used by Telecom for emergency calls in the
+ * rare case that Telephony has not registered any phone accounts yet. Details about this
+ * account are not expected to be displayed in the UI, so the description, etc are not
+ * populated.
+ */
+ static PhoneAccount getDefaultEmergencyPhoneAccount() {
+ return PhoneAccount.builder(DEFAULT_EMERGENCY_PHONE_ACCOUNT_HANDLE, "E")
+ .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+ PhoneAccount.CAPABILITY_CALL_PROVIDER |
+ PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS).build();
+ }
+
static boolean isPstnComponentName(ComponentName componentName) {
final ComponentName pstnComponentName = new ComponentName(
TELEPHONY_PACKAGE_NAME, PSTN_CALL_SERVICE_CLASS_NAME);