Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8267a5e authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13476683 from a1210d2a to 25Q3-release

Change-Id: I91c01c2ab666ee8d39e60e3a0d77674260890168
parents c0b228b5 a1210d2a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -9,6 +9,17 @@ flag {
  bug: "327038818"
}

# OWNER=grantmenke TARGET=25Q3
flag {
  name: "prevent_self_managed_call_logging"
  namespace: "telecom"
  description: "Prevent self managed calls from logging to the call log"
  bug: "405942024"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

# OWNER=pmadapurmath TARGET=25Q3
flag {
  name: "allow_call_on_same_connection_mgr"
+131 −27
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.IAudioService;
@@ -126,6 +127,62 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    private final TelecomSystem.SyncRoot mTelecomLock;
    private CountDownLatch mAudioOperationsCompleteLatch;
    private CountDownLatch mAudioActiveCompleteLatch;

    /** Receiver for added/removed device outputs that are reported by the audio fwk */
    public class AudioRoutesCallback extends AudioDeviceCallback {
        @Override
        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
            Log.startSession("ARC.oADA");
            try {
                updateAudioRoutes(addedDevices, true);
            } finally {
                Log.endSession();
            }
        }

        @Override
        public void onAudioDevicesRemoved(AudioDeviceInfo[] devices) {
            Log.startSession("ARC.oADR");
            try {
                updateAudioRoutes(devices, false);
            } finally {
                Log.endSession();
            }
        }

        private void updateAudioRoutes(AudioDeviceInfo[] devices, boolean addDevices) {
            Log.i(this, "updateAudioRoutes: add devices? %b", addDevices);
            for (AudioDeviceInfo deviceInfo: devices) {
                int audioRouteType = getAudioType(deviceInfo);
                Log.i(this, "updateAudioRoutes: audioDeviceInfo: %s, audioRouteType: %d",
                        deviceInfo, audioRouteType);
                // We should really only worry about handling earpiece and speaker. Bluetooth and
                // wired headset routes are already dynamically updated. This logic can be updated
                // once we support call audio route centralization.
                if (audioRouteType == TYPE_INVALID || audioRouteType == AudioRoute.TYPE_WIRED
                        || BT_AUDIO_ROUTE_TYPES.contains(audioRouteType)) {
                    Log.i(this, "updateAudioRoutes: skipping route.");
                    continue;
                }
                if (addDevices) {
                    switch(audioRouteType) {
                        case AudioRoute.TYPE_SPEAKER:
                            createSpeakerRoute();
                            break;
                        case AudioRoute.TYPE_EARPIECE:
                            createEarpieceRoute();
                            break;
                        default:
                            break;
                    }
                } else {
                    AudioRoute route = mTypeRoutes.remove(audioRouteType);
                    updateAvailableRoutes(route, false);
                }
            }
        }
    }

    private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -187,6 +244,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    private boolean mIsPending;
    private boolean mIsActive;
    private boolean mWasOnSpeaker;
    private AudioRoutesCallback mAudioRoutesCallback;
    private final TelecomMetricsController mMetricsController;

    public CallAudioRouteController(
@@ -404,23 +462,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        int supportMask = calculateSupportedRouteMaskInit();
        if ((supportMask & CallAudioState.ROUTE_SPEAKER) != 0) {
            int audioRouteType = AudioRoute.TYPE_SPEAKER;
            // Create speaker routes
            mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_SPEAKER, null,
                    mAudioManager);
            if (mSpeakerDockRoute == null){
                Log.i(this, "Can't find available audio device info for route TYPE_SPEAKER, trying"
                        + " for TYPE_BUS");
                mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_BUS, null,
                        mAudioManager);
                audioRouteType = AudioRoute.TYPE_BUS;
            }
            if (mSpeakerDockRoute != null) {
                mTypeRoutes.put(audioRouteType, mSpeakerDockRoute);
                updateAvailableRoutes(mSpeakerDockRoute, true);
            } else {
                Log.w(this, "Can't find available audio device info for route TYPE_SPEAKER "
                        + "or TYPE_BUS.");
            }
            createSpeakerRoute();
        }

        if ((supportMask & CallAudioState.ROUTE_WIRED_HEADSET) != 0) {
@@ -434,15 +476,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                updateAvailableRoutes(mEarpieceWiredRoute, true);
            }
        } else if ((supportMask & CallAudioState.ROUTE_EARPIECE) != 0) {
            // Create earpiece routes
            mEarpieceWiredRoute = mAudioRouteFactory.create(AudioRoute.TYPE_EARPIECE, null,
                    mAudioManager);
            if (mEarpieceWiredRoute == null) {
                Log.w(this, "Can't find available audio device info for route TYPE_EARPIECE");
            } else {
                mTypeRoutes.put(AudioRoute.TYPE_EARPIECE, mEarpieceWiredRoute);
                updateAvailableRoutes(mEarpieceWiredRoute, true);
            }
            createEarpieceRoute();
        }

        // set current route
@@ -464,6 +498,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                supportMask, null, new HashSet<>());
        mAudioManager.addOnCommunicationDeviceChangedListener(
                mCommunicationDeviceChangedExecutor, mCommunicationDeviceListener);
        mAudioRoutesCallback = new AudioRoutesCallback();
        mAudioManager.registerAudioDeviceCallback(mAudioRoutesCallback, null);
    }

    @Override
@@ -1427,8 +1463,15 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        } else {
            AudioDeviceInfo[] deviceList = mAudioManager.getDevices(
                    AudioManager.GET_DEVICES_OUTPUTS);
            // For debugging purposes in cases where the device list returned by the API fwk is
            // empty and we don't end up adding the earpiece route upon init.
            Log.i(this, "calculateSupportedRouteMaskInit: is device list size: %d",
                    deviceList.length);
            for (AudioDeviceInfo device: deviceList) {
                if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
                Log.i(this, "calculateSupportedRouteMaskInit: audio route type from audio "
                        + "device info: %d", device != null ? DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE
                        .getOrDefault(device.getType(), TYPE_INVALID) : TYPE_INVALID);
                if (device != null && device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
                    routeMask |= CallAudioState.ROUTE_EARPIECE;
                    break;
                }
@@ -1692,6 +1735,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    }

    private void updateAvailableRoutes(AudioRoute route, boolean includeRoute) {
        if (route == null) {
            return;
        }
        if (includeRoute) {
            mAvailableRoutes.add(route);
        } else {
@@ -1781,4 +1827,62 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        }
        return isDestRouteActive;
    }

    private void createSpeakerRoute() {
        int audioRouteType = TYPE_SPEAKER;
        if (mSpeakerDockRoute == null) {
            //create type speaker
            mSpeakerDockRoute = mAudioRouteFactory.create(audioRouteType, null,
                    mAudioManager);
            // If speaker route couldn't be instantiated, try for TYPE_BUS
            if (mSpeakerDockRoute == null) {
                Log.i(this, "createSpeakerRoute: Can't find available audio device info for "
                        + "route TYPE_SPEAKER, trying for TYPE_BUS");
                mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_BUS, null,
                        mAudioManager);
                audioRouteType = AudioRoute.TYPE_BUS;
            }
            if (mSpeakerDockRoute == null) {
                Log.w(this, "createSpeakerRoute: Can't find available audio device info "
                        + "for route TYPE_SPEAKER or TYPE_BUS.");
            } else {
                // Update available routes
                mTypeRoutes.put(audioRouteType, mSpeakerDockRoute);
                updateAvailableRoutes(mSpeakerDockRoute, true);
            }
        } else {
            Log.i(this, "createSpeakerRoute: route already created. Skipping.");
        }
    }

    private void createEarpieceRoute() {
        // Create earpiece route
        if (mEarpieceWiredRoute != null) {
            Log.i(this, "createEarpieceRoute: route already created. Skipping.");
            return;
        }
        mEarpieceWiredRoute = mAudioRouteFactory.create(AudioRoute.TYPE_EARPIECE, null,
                mAudioManager);
        if (mEarpieceWiredRoute == null) {
            Log.w(this, "createEarpieceRoute: Can't find available audio device info for "
                    + "route TYPE_EARPIECE");
        } else {
            mTypeRoutes.put(AudioRoute.TYPE_EARPIECE, mEarpieceWiredRoute);
            updateAvailableRoutes(mEarpieceWiredRoute, true);
        }
    }

    @VisibleForTesting
    public AudioRoute getAudioRouteForTesting(int audioRouteType) {
        return switch (audioRouteType) {
            case AudioRoute.TYPE_EARPIECE, AudioRoute.TYPE_WIRED -> mEarpieceWiredRoute;
            case AudioRoute.TYPE_SPEAKER -> mSpeakerDockRoute;
            default -> DUMMY_ROUTE;
        };
    }

    @VisibleForTesting
    public AudioRoutesCallback getAudioRoutesCallback() {
        return mAudioRoutesCallback;
    }
}
+20 −2
Original line number Diff line number Diff line
@@ -172,6 +172,24 @@ public final class CallLogManager extends CallsManagerListenerBase {
        }
    }

    /**
     * Log call only if Call is NOT a self-managed call OR call is a self-managed call which has
     * indicated it should be logged in its PhoneAccount
     */
    void logCallIfNotSelfManaged (Call call, int type, boolean showNotificationForMissedCall,
            CallFilteringResult result) {
        boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged() &&
                (call.getHandoverState() == HandoverState.HANDOVER_NONE
                || call.getHandoverState() == HandoverState.HANDOVER_COMPLETE);
        if (!mFeatureFlags.preventSelfManagedCallLogging() || !call.isSelfManaged() ||
                shouldCallSelfManagedLogged) {
            logCall(call, type, showNotificationForMissedCall, result);
        } else {
            Log.d(TAG, "logCallIfNotSelfManaged: skipping call logging due to self managed "
                    + "for call = " + call);
        }
    }

    /**
     * Log newly disconnected calls only if all of below conditions are met:
     * Call was NOT in the "choose account" phase when disconnected
@@ -187,8 +205,8 @@ public final class CallLogManager extends CallsManagerListenerBase {
     */
    @VisibleForTesting
    public boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) {
        boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged()
                && (call.getHandoverState() == HandoverState.HANDOVER_NONE
        boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged() &&
                (call.getHandoverState() == HandoverState.HANDOVER_NONE
                || call.getHandoverState() == HandoverState.HANDOVER_COMPLETE);

        // "Choose account" phase when disconnected
+6 −6
Original line number Diff line number Diff line
@@ -1179,7 +1179,7 @@ public class CallsManager extends Call.ListenerBase
                if (result.shouldShowNotification) {
                    Log.w(this, "onCallScreeningCompleted: blocked call, showing notification.");
                }
                mCallLogManager.logCall(incomingCall, Calls.BLOCKED_TYPE,
                mCallLogManager.logCallIfNotSelfManaged(incomingCall, Calls.BLOCKED_TYPE,
                        result.shouldShowNotification, result);
            }
            if (result.shouldShowNotification) {
@@ -1823,7 +1823,7 @@ public class CallsManager extends Call.ListenerBase
                if (hasMaximumManagedRingingCalls(call)) {
                    call.setMissedReason(AUTO_MISSED_MAXIMUM_RINGING);
                    call.setStartFailCause(CallFailureCause.MAX_RINGING_CALLS);
                    mCallLogManager.logCall(call, Calls.MISSED_TYPE,
                    mCallLogManager.logCallIfNotSelfManaged(call, Calls.MISSED_TYPE,
                            true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
                }
                call.setStartFailCause(startFailCause);
@@ -1836,7 +1836,7 @@ public class CallsManager extends Call.ListenerBase
            call.setMissedReason(AUTO_MISSED_EMERGENCY_CALL);
            call.getAnalytics().setMissedReason(call.getMissedReason());
            call.setStartFailCause(CallFailureCause.IN_EMERGENCY_CALL);
            mCallLogManager.logCall(call, Calls.MISSED_TYPE,
            mCallLogManager.logCallIfNotSelfManaged(call, Calls.MISSED_TYPE,
                    true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
            if (isConference) {
                notifyCreateConferenceFailed(phoneAccountHandle, call);
@@ -1855,7 +1855,7 @@ public class CallsManager extends Call.ListenerBase
                call.setMissedReason(AUTO_MISSED_MAXIMUM_DIALING);
            }
            call.getAnalytics().setMissedReason(call.getMissedReason());
            mCallLogManager.logCall(call, Calls.MISSED_TYPE,
            mCallLogManager.logCallIfNotSelfManaged(call, Calls.MISSED_TYPE,
                    true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
            if (isConference) {
                notifyCreateConferenceFailed(phoneAccountHandle, call);
@@ -4360,7 +4360,7 @@ public class CallsManager extends Call.ListenerBase

        if (oldState == CallState.NEW && disconnectCause.getCode() == DisconnectCause.MISSED) {
            Log.i(this, "markCallAsDisconnected: logging missed call ");
            mCallLogManager.logCall(call, Calls.MISSED_TYPE, true, null);
            mCallLogManager.logCallIfNotSelfManaged(call, Calls.MISSED_TYPE, true, null);
        }
    }

@@ -4879,7 +4879,7 @@ public class CallsManager extends Call.ListenerBase
        // Since the call was not added to the list of calls, we have to call the missed
        // call notifier and the call logger manually.
        // Do we need missed call notification for direct to Voicemail calls?
        mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
        mCallLogManager.logCallIfNotSelfManaged(incomingCall, Calls.MISSED_TYPE,
                true /*showNotificationForMissedCall*/, result);
    }

+41 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_SPEAK
import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -1535,6 +1537,45 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        BLUETOOTH_DEVICES.remove(scoDevice);
    }

    @Test
    @SmallTest
    public void testAddAudioRoutesDynamic() {
        AudioRoute.Factory audioRouteFactory = new AudioRoute.Factory() {
            @Override
            public AudioRoute create(@AudioRoute.AudioRouteType int type, String bluetoothAddress,
                    AudioManager audioManager) {
                if (mOverrideSpeakerToBus && type == AudioRoute.TYPE_SPEAKER) {
                    type = AudioRoute.TYPE_BUS;
                }
                // Purposely return null to mimic audio routes not being created upon
                // initialization.
                return null;
            }
        };
        mController.setAudioRouteFactory(audioRouteFactory);
        mController.initialize();
        // Verify that the earpiece/speaker routes aren't created upon initialization of the
        // controller.
        assertNull(mController.getAudioRouteForTesting(AudioRoute.TYPE_SPEAKER));
        assertNull(mController.getAudioRouteForTesting(AudioRoute.TYPE_EARPIECE));

        // Set up the AudioDeviceCallback to signal to the controller of the newly added devices
        // (earpiece + speaker).
        CallAudioRouteController.AudioRoutesCallback callback = mController
                .getAudioRoutesCallback();
        AudioDeviceInfo earpieceDeviceInfo = mock(AudioDeviceInfo.class);
        when(earpieceDeviceInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
        AudioDeviceInfo speakerDeviceInfo = mock(AudioDeviceInfo.class);
        when(speakerDeviceInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);

        // Reset the audio route factory so that the route creation can be successful now.
        mController.setAudioRouteFactory(mAudioRouteFactory);
        callback.onAudioDevicesAdded(new AudioDeviceInfo[] {earpieceDeviceInfo, speakerDeviceInfo});
        // Verify that the earpiece/speaker routes are created this time around.
        assertNotNull(mController.getAudioRouteForTesting(AudioRoute.TYPE_SPEAKER));
        assertNotNull(mController.getAudioRouteForTesting(AudioRoute.TYPE_EARPIECE));
    }

    private void verifyConnectBluetoothDevice(int audioType) {
        mController.initialize();
        mController.setActive(true);