[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 3a78ed748b -s ours am: 1f701514e4 -s ours am: 53ee848458 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/24212729
Change-Id: I51d199071390b53f6244f83265d3d3ab9834270c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 3005656..d085be6 100644
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -19,9 +19,12 @@
import static android.provider.CallLog.Calls.BLOCK_REASON_NOT_BLOCKED;
import static android.telephony.CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.location.Country;
import android.location.CountryDetector;
import android.location.Location;
@@ -42,6 +45,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.callfiltering.CallFilteringResult;
@@ -49,6 +53,7 @@
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
+import java.util.UUID;
import java.util.stream.Stream;
/**
@@ -68,16 +73,19 @@
*/
private static class AddCallArgs {
public AddCallArgs(Context context, CallLog.AddCallParams params,
- @Nullable LogCallCompletedListener logCallCompletedListener) {
+ @Nullable LogCallCompletedListener logCallCompletedListener,
+ @NonNull String callId) {
this.context = context;
this.params = params;
this.logCallCompletedListener = logCallCompletedListener;
+ this.callId = callId;
}
// Since the members are accessed directly, we don't use the
// mXxxx notation.
public final Context context;
public final CallLog.AddCallParams params;
+ public final String callId;
@Nullable
public final LogCallCompletedListener logCallCompletedListener;
}
@@ -88,11 +96,16 @@
// TODO: come up with a better way to indicate in a android.telecom.DisconnectCause that
// a conference was merged successfully
private static final String REASON_IMS_MERGED_SUCCESSFULLY = "IMS_MERGED_SUCCESSFULLY";
+ private static final UUID LOG_CALL_FAILED_ANOMALY_ID =
+ UUID.fromString("1c4c15f3-ab4f-459c-b9ef-43d2988bae82");
+ private static final String LOG_CALL_FAileD_ANOMALY_DESC =
+ "Failed to record a call to the call log.";
private final Context mContext;
private final CarrierConfigManager mCarrierConfigManager;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final MissedCallNotifier mMissedCallNotifier;
+ private AnomalyReporterAdapter mAnomalyReporterAdapter;
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 =
@@ -104,12 +117,13 @@
private String mCurrentCountryIso;
public CallLogManager(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
- MissedCallNotifier missedCallNotifier) {
+ MissedCallNotifier missedCallNotifier, AnomalyReporterAdapter anomalyReporterAdapter) {
mContext = context;
mCarrierConfigManager = (CarrierConfigManager) mContext
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
mPhoneAccountRegistrar = phoneAccountRegistrar;
mMissedCallNotifier = missedCallNotifier;
+ mAnomalyReporterAdapter = anomalyReporterAdapter;
mLock = new Object();
}
@@ -385,7 +399,7 @@
okayToLogCall(accountHandle, logNumber, call.isEmergencyCall());
if (okayToLog) {
AddCallArgs args = new AddCallArgs(mContext, paramBuilder.build(),
- logCallCompletedListener);
+ logCallCompletedListener, call.getId());
Log.addEvent(call, LogUtils.Events.LOG_CALL, "number=" + Log.piiHandle(logNumber)
+ ",postDial=" + Log.piiHandle(call.getPostDialDigits()) + ",pres="
+ call.getHandlePresentation());
@@ -515,9 +529,27 @@
for (int i = 0; i < count; i++) {
AddCallArgs c = callList[i];
mListeners[i] = c.logCallCompletedListener;
+
try {
// May block.
+ ContentResolver resolver = c.context.getContentResolver();
+ Pair<Integer, Integer> startStats = getCallLogStats(resolver);
result[i] = Calls.addCall(c.context, c.params);
+ Pair<Integer, Integer> endStats = getCallLogStats(resolver);
+
+ Log.i(TAG, "LogCallAsync; logged callId=%s, uri=%s, "
+ + "startCount=%d, startMaxId=%d, endCount=%d, endMaxId=%s",
+ c.callId, result[i], startStats.first, startStats.second,
+ endStats.first, endStats.second);
+ if ((endStats.second - startStats.second) <= 0) {
+ // No call was added or even worse we lost a call in the log. Trigger an
+ // anomaly report. Note: it technically possible that an app modified the
+ // call log while we were writing to it here; that is pretty unlikely, and
+ // the goal here is to try and identify potential anomalous conditions with
+ // logging calls.
+ mAnomalyReporterAdapter.reportAnomaly(LOG_CALL_FAILED_ANOMALY_ID,
+ LOG_CALL_FAileD_ANOMALY_DESC);
+ }
} catch (Exception e) {
// This is very rare but may happen in legitimate cases.
// E.g. If the phone is encrypted and thus write request fails, it may cause
@@ -526,8 +558,10 @@
//
// We don't want to crash the whole process just because of that, so just log
// it instead.
- Log.e(TAG, e, "Exception raised during adding CallLog entry.");
+ Log.e(TAG, e, "LogCallAsync: Exception raised adding callId=%s", c.callId);
result[i] = null;
+ mAnomalyReporterAdapter.reportAnomaly(LOG_CALL_FAILED_ANOMALY_ID,
+ LOG_CALL_FAileD_ANOMALY_DESC);
}
}
return result;
@@ -544,7 +578,7 @@
failed.
*/
if (uri == null) {
- Log.w(TAG, "Failed to write call to the log.");
+ Log.w(TAG, "LogCallAsync: Failed to write call to the log.");
}
if (mListeners[i] != null) {
mListeners[i].onLogCompleted(uri);
@@ -602,4 +636,40 @@
return mCurrentCountryIso;
}
}
+
+ /**
+ * Returns a pair containing the number of rows in the call log, as well as the maximum call log
+ * ID. There is a limit of 500 entries in the call log for a phone account, so once we hit 500
+ * we can reasonably expect that number to not change before and after logging a call.
+ * We determine the maximum ID in the call log since this is a way we can objectively check if
+ * the provider did record a call log entry or not. Ideally there should me more call log
+ * entries after logging than before, and certainly not less.
+ * @param resolver content resolver
+ * @return pair with number of rows in the call log and max id.
+ */
+ private Pair<Integer, Integer> getCallLogStats(@NonNull ContentResolver resolver) {
+ try {
+ int maxCallId = -1;
+ Cursor countCursor = resolver.query(Calls.CONTENT_URI,
+ new String[]{Calls._ID},
+ null,
+ null,
+ Calls._ID + " DESC");
+ int numFound = countCursor.getCount();
+ if (numFound > 0) {
+ countCursor.moveToFirst();
+ maxCallId = countCursor.getInt(0);
+ }
+ return new Pair<>(numFound, maxCallId);
+ } catch (Exception e) {
+ // Oh jeepers, we crashed getting the call count.
+ Log.e(TAG, e, "getCountOfCallLogRows: failed");
+ return new Pair<>(-1, -1);
+ }
+ }
+
+ @VisibleForTesting
+ public void setAnomalyReporterAdapter(AnomalyReporterAdapter anomalyReporterAdapter){
+ mAnomalyReporterAdapter = anomalyReporterAdapter;
+ }
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index a57449d..4f4f20c 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -655,7 +655,8 @@
mTtyManager = new TtyManager(context, mWiredHeadsetManager);
mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
- mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
+ mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier,
+ mAnomalyReporter);
mConnectionServiceRepository =
new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
@@ -1381,8 +1382,11 @@
}
@VisibleForTesting
- public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
- mAnomalyReporter = mAnomalyReporterAdapter;
+ public void setAnomalyReporterAdapter(AnomalyReporterAdapter anomalyReporterAdapter){
+ mAnomalyReporter = anomalyReporterAdapter;
+ if (mCallLogManager != null) {
+ mCallLogManager.setAnomalyReporterAdapter(anomalyReporterAdapter);
+ }
}
void processIncomingConference(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
@@ -4739,15 +4743,21 @@
/**
* Determines the number of unholdable calls present in a connection service other than the one
- * the passed phone account belonds to.
+ * the passed phone account belongs to. If a ConnectionService has not been associated with an
+ * outgoing call yet (for example, it is in the SELECT_PHONE_ACCOUNT state), then we do not
+ * count that call because it is not tracked as an active call yet.
* @param phoneAccountHandle The handle of the PhoneAccount.
* @return Number of unholdable calls owned by other connection service.
*/
public int getNumUnholdableCallsForOtherConnectionService(
PhoneAccountHandle phoneAccountHandle) {
return (int) mCalls.stream().filter(call ->
- !phoneAccountHandle.getComponentName().equals(
- call.getTargetPhoneAccount().getComponentName())
+ // If this convention needs to be changed, answerCall will need to be modified to
+ // change what an "active call" is so that the call in SELECT_PHONE_ACCOUNT state
+ // will be properly cancelled.
+ call.getTargetPhoneAccount() != null
+ && !phoneAccountHandle.getComponentName().equals(
+ call.getTargetPhoneAccount().getComponentName())
&& call.getParentCall() == null
&& !call.isExternalCall()
&& !canHold(call)).count();
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index f33b185..7d3eeb6 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -2707,6 +2707,7 @@
int packageUid = -1;
int callingUid = Binder.getCallingUid();
PackageManager pm;
+ long token = Binder.clearCallingIdentity();
try{
pm = mContext.createContextAsUser(
UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
@@ -2715,6 +2716,8 @@
Log.i(this, "callingUidMatchesPackageManagerRecords:"
+ " createContextAsUser hit exception=[%s]", e.toString());
return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
if (pm != null) {
try {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 473e7b9..20bca3d 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -28,7 +28,9 @@
import android.media.AudioManager;
import android.media.AudioDeviceInfo;
import android.media.audio.common.AudioDevice;
+import android.os.Bundle;
import android.telecom.Log;
+import android.util.ArraySet;
import android.util.LocalLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -101,9 +103,13 @@
logString = "Got BluetoothLeAudio: "
+ mBluetoothLeAudioService;
if (!mLeAudioCallbackRegistered) {
- mBluetoothLeAudioService.registerCallback(
- mExecutor, mLeAudioCallbacks);
- mLeAudioCallbackRegistered = true;
+ try {
+ mBluetoothLeAudioService.registerCallback(
+ mExecutor, mLeAudioCallbacks);
+ mLeAudioCallbackRegistered = true;
+ } catch (IllegalStateException e) {
+ logString += ", but Bluetooth is down";
+ }
}
} else {
logString = "Connected to non-requested bluetooth service." +
@@ -234,18 +240,38 @@
}
public int getNumConnectedDevices() {
- synchronized (mLock) {
- return mHfpDevicesByAddress.size() +
- mHearingAidDevicesByAddress.size() +
- getLeAudioConnectedDevices().size();
- }
+ return getConnectedDevices().size();
}
public Collection<BluetoothDevice> getConnectedDevices() {
synchronized (mLock) {
- ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values());
+ ArraySet<BluetoothDevice> result = new ArraySet<>();
+
+ // Set storing the group ids of all dual mode audio devices to de-dupe them
+ Set<Integer> dualModeGroupIds = new ArraySet<>();
+ for (BluetoothDevice hfpDevice: mHfpDevicesByAddress.values()) {
+ result.add(hfpDevice);
+ if (mBluetoothLeAudioService == null) {
+ continue;
+ }
+ int groupId = mBluetoothLeAudioService.getGroupId(hfpDevice);
+ if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
+ dualModeGroupIds.add(groupId);
+ }
+ }
+
result.addAll(mHearingAidDevicesByAddress.values());
- result.addAll(getLeAudioConnectedDevices());
+ if (mBluetoothLeAudioService == null) {
+ return Collections.unmodifiableCollection(result);
+ }
+ for (BluetoothDevice leAudioDevice: getLeAudioConnectedDevices()) {
+ // Exclude dual mode audio devices included from the HFP devices list
+ int groupId = mBluetoothLeAudioService.getGroupId(leAudioDevice);
+ if (groupId != BluetoothLeAudio.GROUP_ID_INVALID
+ && !dualModeGroupIds.contains(groupId)) {
+ result.add(leAudioDevice);
+ }
+ }
return Collections.unmodifiableCollection(result);
}
}
@@ -253,9 +279,9 @@
// Same as getConnectedDevices except it filters out the hearing aid devices that are linked
// together by their hiSyncId.
public Collection<BluetoothDevice> getUniqueConnectedDevices() {
- ArrayList<BluetoothDevice> result;
+ ArraySet<BluetoothDevice> result;
synchronized (mLock) {
- result = new ArrayList<>(mHfpDevicesByAddress.values());
+ result = new ArraySet<>(mHfpDevicesByAddress.values());
}
Set<Long> seenHiSyncIds = new LinkedHashSet<>();
// Add the left-most active device to the seen list so that we match up with the list
@@ -367,6 +393,8 @@
return;
}
if (!targetDeviceMap.containsKey(device.getAddress())) {
+ Log.i(this, "Adding device with address: " + device + " and devicetype="
+ + getDeviceTypeString(deviceType));
targetDeviceMap.put(device.getAddress(), device);
mBluetoothRouteManager.onDeviceAdded(device.getAddress());
}
@@ -391,6 +419,8 @@
return;
}
if (targetDeviceMap.containsKey(device.getAddress())) {
+ Log.i(this, "Removing device with address: " + device + " and devicetype="
+ + getDeviceTypeString(deviceType));
targetDeviceMap.remove(device.getAddress());
mBluetoothRouteManager.onDeviceLost(device.getAddress());
}
@@ -568,50 +598,72 @@
// Connect audio to the bluetooth device at address, checking to see whether it's
// le audio, hearing aid or a HFP device, and using the proper BT API.
public boolean connectAudio(String address, boolean switchingBtDevices) {
+ int callProfile = BluetoothProfile.LE_AUDIO;
+ Log.i(this, "Telecomm connecting audio to device: " + address);
+ BluetoothDevice device = null;
if (mLeAudioDevicesByAddress.containsKey(address)) {
+ Log.i(this, "Telecomm found LE Audio device for address: " + address);
if (mBluetoothLeAudioService == null) {
Log.w(this, "Attempting to turn on audio when the le audio service is null");
return false;
}
- BluetoothDevice device = mLeAudioDevicesByAddress.get(address);
+ device = mLeAudioDevicesByAddress.get(address);
+ callProfile = BluetoothProfile.LE_AUDIO;
+ } else if (mHearingAidDevicesByAddress.containsKey(address)) {
+ Log.i(this, "Telecomm found hearing aid device for address: " + address);
+ if (mBluetoothHearingAid == null) {
+ Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
+ return false;
+ }
+ device = mHearingAidDevicesByAddress.get(address);
+ callProfile = BluetoothProfile.HEARING_AID;
+ } else if (mHfpDevicesByAddress.containsKey(address)) {
+ Log.i(this, "Telecomm found HFP device for address: " + address);
+ if (mBluetoothHeadset == null) {
+ Log.w(this, "Attempting to turn on audio when the headset service is null");
+ return false;
+ }
+ device = mHfpDevicesByAddress.get(address);
+ callProfile = BluetoothProfile.HEADSET;
+ }
+
+ if (device == null) {
+ Log.w(this, "No active profiles for Bluetooth address=" + address);
+ return false;
+ }
+
+ Bundle preferredAudioProfiles = mBluetoothAdapter.getPreferredAudioProfiles(device);
+ if (preferredAudioProfiles != null && !preferredAudioProfiles.isEmpty()
+ && preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX) != 0) {
+ Log.i(this, "Preferred duplex profile for device=" + address + " is "
+ + preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ callProfile = preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX);
+ }
+
+ if (callProfile == BluetoothProfile.LE_AUDIO) {
if (mBluetoothAdapter.setActiveDevice(
device, BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
-
/* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
* Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
* will be audio switched to is available to be choose as communication device */
if (!switchingBtDevices) {
return setLeAudioCommunicationDevice();
}
-
return true;
}
return false;
- } else if (mHearingAidDevicesByAddress.containsKey(address)) {
- if (mBluetoothHearingAid == null) {
- Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
- return false;
- }
- if (mBluetoothAdapter.setActiveDevice(
- mHearingAidDevicesByAddress.get(address),
- BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
-
+ } else if (callProfile == BluetoothProfile.HEARING_AID) {
+ if (mBluetoothAdapter.setActiveDevice(device, BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
/* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
* Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
* will be audio switched to is available to be choose as communication device */
if (!switchingBtDevices) {
return setHearingAidCommunicationDevice();
}
-
return true;
}
return false;
- } else if (mHfpDevicesByAddress.containsKey(address)) {
- BluetoothDevice device = mHfpDevicesByAddress.get(address);
- if (mBluetoothHeadset == null) {
- Log.w(this, "Attempting to turn on audio when the headset service is null");
- return false;
- }
+ } else if (callProfile == BluetoothProfile.HEADSET) {
boolean success = mBluetoothAdapter.setActiveDevice(device,
BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
if (!success) {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 20af7b5..09b8f76 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -16,6 +16,7 @@
package com.android.server.telecom.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
@@ -25,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
import android.telecom.Log;
import android.telecom.Logging.Session;
@@ -84,7 +86,7 @@
intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
BluetoothDevice device =
- intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
if (device == null) {
Log.w(LOG_TAG, "Got null device from broadcast. " +
"Ignoring.");
@@ -115,7 +117,7 @@
int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
BluetoothHeadset.STATE_DISCONNECTED);
BluetoothDevice device =
- intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
if (device == null) {
Log.w(LOG_TAG, "Got null device from broadcast. " +
@@ -149,7 +151,7 @@
private void handleActiveDeviceChanged(Intent intent) {
BluetoothDevice device =
- intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
int deviceType;
if (BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
@@ -181,11 +183,31 @@
}
args.arg2 = device.getAddress();
+ boolean usePreferredAudioProfile = false;
+ BluetoothAdapter bluetoothAdapter = mBluetoothDeviceManager.getBluetoothAdapter();
+ int preferredDuplexProfile = BluetoothProfile.LE_AUDIO;
+ if (bluetoothAdapter != null) {
+ Bundle preferredAudioProfiles = bluetoothAdapter.getPreferredAudioProfiles(
+ device);
+ if (preferredAudioProfiles != null && !preferredAudioProfiles.isEmpty()
+ && preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)
+ != 0) {
+ Log.i(this, "Preferred duplex profile for device=" + device + " is "
+ + preferredAudioProfiles.getInt(
+ BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ usePreferredAudioProfile = true;
+ preferredDuplexProfile =
+ preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX);
+ }
+ }
+
if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) {
/* In Le Audio case, once device got Active, the Telecom needs to make sure it
* is set as communication device before we can say that BT_AUDIO_IS_ON
*/
- if (!mBluetoothDeviceManager.setLeAudioCommunicationDevice()) {
+ if ((!usePreferredAudioProfile
+ || preferredDuplexProfile == BluetoothProfile.LE_AUDIO)
+ && !mBluetoothDeviceManager.setLeAudioCommunicationDevice()) {
Log.w(LOG_TAG,
"Device %s cannot be use as LE audio communication device.",
device);
diff --git a/src/com/android/server/telecom/callfiltering/BlockCheckerFilter.java b/src/com/android/server/telecom/callfiltering/BlockCheckerFilter.java
index 36f2077..64060c8 100644
--- a/src/com/android/server/telecom/callfiltering/BlockCheckerFilter.java
+++ b/src/com/android/server/telecom/callfiltering/BlockCheckerFilter.java
@@ -126,6 +126,7 @@
.setShouldReject(true)
.setShouldAddToCallLog(true)
.setShouldShowNotification(false)
+ .setShouldSilence(true)
.setCallBlockReason(getBlockReason(blockStatus))
.setCallScreeningAppName(null)
.setCallScreeningComponentName(null)
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index 749d236..77ee15e 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -101,4 +101,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_contact_discovery" />
+
+ <Button
+ android:id="@+id/add_call_log_entries"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Fill Call Log" />
</LinearLayout>
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index f17af2c..1b82f39 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -6,6 +6,7 @@
import android.app.UiModeManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +32,7 @@
public class TestDialerActivity extends Activity {
private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
+ private static int sEntryNumber = 1;
private EditText mNumberView;
private CheckBox mRttCheckbox;
@@ -78,6 +80,13 @@
}
});
+ findViewById(R.id.add_call_log_entries).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ addCallLogEntries();
+ }
+ });
+
mNumberView = (EditText) findViewById(R.id.number);
mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
mComposerCheckbox = (CheckBox) findViewById(R.id.add_composer_attachments_checkbox);
@@ -237,4 +246,23 @@
intent.putExtra(Settings.EXTRA_SUB_ID, SubscriptionManager.getDefaultSubscriptionId());
startActivity(intent);
}
+
+ private void addCallLogEntries() {
+ ContentResolver contentResolver = getContentResolver();
+ for (int ix = 0; ix < 1000; ix ++) {
+ ContentValues values = new ContentValues();
+ String number = String.format("1650%07d", sEntryNumber++);
+ values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
+ values.put(Calls.NUMBER, number);
+ values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
+ values.put(Calls.DATE, System.currentTimeMillis());
+ values.put(Calls.DURATION, 10);
+ values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, "com.android.phone/com.android.services.telephony.TelephonyConnectionService");
+ values.put(Calls.PHONE_ACCOUNT_ID, 1);
+ values.put(Calls.PHONE_ACCOUNT_ADDRESS, "650-555-1212");
+ values.put(Calls.NEW, 1);
+ contentResolver.insert(Calls.CONTENT_URI, values);
+ }
+
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/BlockCheckerFilterTest.java b/tests/src/com/android/server/telecom/tests/BlockCheckerFilterTest.java
index 7df4f29..a98c1ee 100644
--- a/tests/src/com/android/server/telecom/tests/BlockCheckerFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/BlockCheckerFilterTest.java
@@ -72,6 +72,7 @@
private static final CallFilteringResult BLOCK_RESULT = new CallFilteringResult.Builder()
.setShouldAllowCall(false)
.setShouldReject(true)
+ .setShouldSilence(true)
.setShouldAddToCallLog(true)
.setShouldShowNotification(false)
.setCallBlockReason(CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER)
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index c37d136..943aac1 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.os.Bundle;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
@@ -178,6 +179,7 @@
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
+ when(mBluetoothLeAudio.getGroupId(device5)).thenReturn(1);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(1)).thenReturn(device5);
receiverUnderTest.onReceive(mContext,
@@ -188,6 +190,7 @@
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 2);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(2)).thenReturn(device6);
+ when(mBluetoothLeAudio.getGroupId(device6)).thenReturn(1);
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
@@ -263,17 +266,19 @@
@Test
public void testLeAudioDedup() {
receiverUnderTest.onReceive(mContext,
- buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+ buildConnectionActionIntent(BluetoothProfile.STATE_CONNECTED, device1,
BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
receiverUnderTest.onReceive(mContext,
- buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+ buildConnectionActionIntent(BluetoothProfile.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
receiverUnderTest.onReceive(mContext,
- buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device6,
+ buildConnectionActionIntent(BluetoothProfile.STATE_CONNECTED, device6,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 1);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(1)).thenReturn(device5);
+ when(mBluetoothLeAudio.getGroupId(device5)).thenReturn(1);
+ when(mBluetoothLeAudio.getGroupId(device6)).thenReturn(1);
assertEquals(2, mBluetoothDeviceManager.getNumConnectedDevices());
assertEquals(2, mBluetoothDeviceManager.getUniqueConnectedDevices().size());
}
@@ -458,6 +463,8 @@
verify(mBluetoothHeadset, never()).connectAudio();
verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+ verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_AUDIO));
receiverUnderTest.onReceive(mContext, buildActiveDeviceChangeActionIntent(device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
@@ -485,6 +492,8 @@
verify(mBluetoothHeadset, never()).connectAudio();
verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+ verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
when(mAdapter.getActiveDevices(eq(BluetoothProfile.LE_AUDIO)))
.thenReturn(Arrays.asList(device5, device6));
@@ -499,6 +508,98 @@
@SmallTest
@Test
+ public void testConnectDualModeEarbud() {
+ receiverUnderTest.setIsInCall(true);
+
+ // LE Audio earbuds connected
+ receiverUnderTest.onReceive(mContext,
+ buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+ BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
+ receiverUnderTest.onReceive(mContext,
+ buildConnectionActionIntent(BluetoothLeAudio.STATE_CONNECTED, device6,
+ BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 1);
+ // HFP device connected
+ receiverUnderTest.onReceive(mContext,
+ buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+ BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+ when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
+
+ AudioDeviceInfo mockAudioDevice5Info = mock(AudioDeviceInfo.class);
+ when(mockAudioDevice5Info.getAddress()).thenReturn(device5.getAddress());
+ when(mockAudioDevice5Info.getType()).thenReturn(AudioDeviceInfo.TYPE_BLE_HEADSET);
+ AudioDeviceInfo mockAudioDevice6Info = mock(AudioDeviceInfo.class);
+ when(mockAudioDevice6Info.getAddress()).thenReturn(device6.getAddress());
+ when(mockAudioDevice6Info.getType()).thenReturn(AudioDeviceInfo.TYPE_BLE_HEADSET);
+ List<AudioDeviceInfo> devices = new ArrayList<>();
+ devices.add(mockAudioDevice5Info);
+ devices.add(mockAudioDevice6Info);
+
+ when(mockAudioManager.getAvailableCommunicationDevices())
+ .thenReturn(devices);
+ when(mockAudioManager.setCommunicationDevice(mockAudioDevice5Info))
+ .thenReturn(true);
+
+ Bundle hfpPreferred = new Bundle();
+ hfpPreferred.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.HEADSET);
+ Bundle leAudioPreferred = new Bundle();
+ leAudioPreferred.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.LE_AUDIO);
+
+ // TEST 1: LE Audio preferred for DUPLEX
+ when(mAdapter.getPreferredAudioProfiles(device5)).thenReturn(leAudioPreferred);
+ when(mAdapter.getPreferredAudioProfiles(device6)).thenReturn(leAudioPreferred);
+ mBluetoothDeviceManager.connectAudio(device5.getAddress(), false);
+ verify(mAdapter, times(1)).setActiveDevice(device5, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+ verify(mBluetoothHeadset, never()).connectAudio();
+ verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+ verify(mockAudioManager).setCommunicationDevice(mockAudioDevice5Info);
+
+ when(mAdapter.getActiveDevices(eq(BluetoothProfile.LE_AUDIO)))
+ .thenReturn(Arrays.asList(device5, device6));
+
+ // Check disconnect during a call
+ devices.remove(mockAudioDevice5Info);
+ receiverUnderTest.onReceive(mContext,
+ buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device5,
+ BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+ leAudioCallbacksTest.getValue().onGroupNodeRemoved(device5, 1);
+
+ mBluetoothDeviceManager.connectAudio(device6.getAddress(), false);
+ verify(mAdapter).setActiveDevice(device6, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+ verify(mBluetoothHeadset, never()).connectAudio();
+ verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+
+ // Reconnect other LE Audio earbud
+ devices.add(mockAudioDevice5Info);
+ receiverUnderTest.onReceive(mContext,
+ buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+ BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
+
+ // Disconnects audio
+ mBluetoothDeviceManager.disconnectAudio();
+ verify(mockAudioManager, times(1)).clearCommunicationDevice();
+ verify(mBluetoothHeadset, times(1)).disconnectAudio();
+
+ // TEST 2: HFP preferred for DUPLEX
+ when(mAdapter.getPreferredAudioProfiles(device5)).thenReturn(hfpPreferred);
+ when(mAdapter.getPreferredAudioProfiles(device6)).thenReturn(hfpPreferred);
+ when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
+ eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL))).thenReturn(true);
+ mBluetoothDeviceManager.connectAudio(device5.getAddress(), false);
+ verify(mAdapter).setActiveDevice(device5, BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
+ verify(mAdapter, times(1)).setActiveDevice(device5, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+ verify(mBluetoothHeadset).connectAudio();
+ mBluetoothDeviceManager.disconnectAudio();
+ verify(mBluetoothHeadset, times(2)).disconnectAudio();
+ }
+
+ @SmallTest
+ @Test
public void testClearHearingAidCommunicationDevice() {
AudioDeviceInfo mockAudioDeviceInfo = mock(AudioDeviceInfo.class);
when(mockAudioDeviceInfo.getAddress()).thenReturn(DEVICE_ADDRESS_1);
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index 9466220..c68cbbf 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -65,6 +65,7 @@
import androidx.test.filters.FlakyTest;
import com.android.server.telecom.Analytics;
+import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallLogManager;
import com.android.server.telecom.CallState;
@@ -123,6 +124,8 @@
PhoneAccountRegistrar mMockPhoneAccountRegistrar;
@Mock
MissedCallNotifier mMissedCallNotifier;
+ @Mock
+ AnomalyReporterAdapter mAnomalyReporterAdapter;
@Override
@Before
@@ -130,7 +133,7 @@
super.setUp();
mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
mCallLogManager = new CallLogManager(mContext, mMockPhoneAccountRegistrar,
- mMissedCallNotifier);
+ mMissedCallNotifier, mAnomalyReporterAdapter);
mDefaultAccountHandle = new PhoneAccountHandle(
new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
TEST_PHONE_ACCOUNT_ID,