[automerger skipped] Merge "Merge rvc-release RP1A.201105.002 to stage-aosp-master - DO NOT MERGE" into stage-aosp-master am: 2d840cb1f4 -s ours am: 434d2717f4 -s ours
am skip reason: Change-Id Iabe3b5a543b465b8e508c61dbba65286b9f68d2c with SHA-1 66f1fabe47 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/12984816
Change-Id: I2c5276164c5e23eeb07b211a176e33598611210e
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 4972602..b53a557 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -173,6 +173,7 @@
void onConnectionTimeChanged(Call call);
void onConferenceStateChanged(Call call, boolean isConference);
void onCdmaConferenceSwap(Call call);
+ void onSetCamera(Call call, String cameraId);
}
/** Interface used to define the action which is executed delay under some condition. */
@@ -1011,6 +1012,18 @@
}
}
+ /**
+ * Handles a change to the currently active camera for a call by notifying listeners.
+ * @param call The call.
+ * @param cameraId The ID of the camera in use, or {@code null} if no camera is in use.
+ */
+ @Override
+ public void onSetCamera(Call call, String cameraId) {
+ for (CallsManagerListener listener : mListeners) {
+ listener.onSetCamera(call, cameraId);
+ }
+ }
+
public Collection<Call> getCalls() {
return Collections.unmodifiableCollection(mCalls);
}
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index e0d2831..55c7b53 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -104,4 +104,8 @@
@Override
public void onCdmaConferenceSwap(Call call) {
}
+
+ @Override
+ public void onSetCamera(Call call, String cameraId) {
+ }
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index e76aef6..9a1c876 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -221,7 +221,7 @@
Log.startSession("ICSBC.oSD", Log.getPackageAbbreviation(name));
synchronized (mLock) {
try {
- Log.d(this, "onDisconnected: %s", name);
+ Log.d(this, "onServiceDisconnected: %s", name);
mIsBound = false;
onDisconnected();
} finally {
@@ -342,6 +342,8 @@
mInCallServiceInfo.getDisconnectTime()
- mInCallServiceInfo.getBindingStartTime(), mIsNullBinding);
}
+
+ InCallController.this.onDisconnected(mInCallServiceInfo);
} else {
Log.i(InCallController.this, "ICSBC#disconnect: already disconnected; %s",
mInCallServiceInfo);
@@ -952,6 +954,17 @@
private final CarModeTracker mCarModeTracker;
+ /**
+ * The package name of the app which is showing the calling UX.
+ */
+ private String mCurrentUserInterfacePackageName = null;
+
+ /**
+ * {@code true} if InCallController is tracking a managed, not external call which is using the
+ * microphone, {@code false} otherwise.
+ */
+ private boolean mIsCallUsingMicrophone = false;
+
public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
SystemStateHelper systemStateHelper,
DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
@@ -1045,6 +1058,7 @@
}
call.removeListener(mCallListener);
mCallIdMapper.removeCall(call);
+ maybeTrackMicrophoneUse(isMuted());
}
@Override
@@ -1120,10 +1134,12 @@
}
Log.i(this, "External call removed from components: %s", componentsUpdated);
}
+ maybeTrackMicrophoneUse(isMuted());
}
@Override
public void onCallStateChanged(Call call, int oldState, int newState) {
+ maybeTrackMicrophoneUse(isMuted());
updateCall(call);
}
@@ -1141,6 +1157,7 @@
if (!mInCallServices.isEmpty()) {
Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldCallAudioState,
newCallAudioState);
+ maybeTrackMicrophoneUse(newCallAudioState.isMuted());
for (IInCallService inCallService : mInCallServices.values()) {
try {
inCallService.onCallAudioStateChanged(newCallAudioState);
@@ -1205,6 +1222,23 @@
updateCall(call);
}
+ /**
+ * Track changes to camera usage for a call.
+ * @param call The call.
+ * @param cameraId The id of the camera to use, or {@code null} if camera is off.
+ */
+ @Override
+ public void onSetCamera(Call call, String cameraId) {
+ Log.i(this, "onSetCamera callId=%s, cameraId=%s", call.getId(), cameraId);
+ if (cameraId != null) {
+ mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+ mContext.getOpPackageName(), false, null, null);
+ } else {
+ mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+ mContext.getOpPackageName(), null);
+ }
+ }
+
void bringToForeground(boolean showDialpad) {
if (!mInCallServices.isEmpty()) {
for (IInCallService inCallService : mInCallServices.values()) {
@@ -1590,6 +1624,11 @@
private boolean onConnected(InCallServiceInfo info, IBinder service) {
Log.i(this, "onConnected to %s", info.getComponentName());
+ if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+ || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+ || info.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+ trackCallingUserInterfaceStarted(info);
+ }
IInCallService inCallService = IInCallService.Stub.asInterface(service);
mInCallServices.put(info, inCallService);
@@ -1657,7 +1696,11 @@
*/
private void onDisconnected(InCallServiceInfo disconnectedInfo) {
Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
-
+ if (disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+ || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+ || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+ trackCallingUserInterfaceStopped(disconnectedInfo);
+ }
mInCallServices.remove(disconnectedInfo);
}
@@ -1724,6 +1767,7 @@
mCallIdMapper.addCall(call);
call.addListener(mCallListener);
}
+ maybeTrackMicrophoneUse(isMuted());
}
/**
@@ -1903,6 +1947,69 @@
}
}
+ /**
+ * Tracks start of microphone use on binding to the current calling UX.
+ * @param info
+ */
+ private void trackCallingUserInterfaceStarted(InCallServiceInfo info) {
+ String packageName = info.getComponentName().getPackageName();
+ if (!Objects.equals(mCurrentUserInterfacePackageName, packageName)) {
+ Log.i(this, "trackCallingUserInterfaceStarted: %s is now calling UX.", packageName);
+ mCurrentUserInterfacePackageName = packageName;
+ }
+ maybeTrackMicrophoneUse(isMuted());
+ }
+
+ /**
+ * Tracks stop of microphone use on unbind from the current calling UX.
+ * @param info
+ */
+ private void trackCallingUserInterfaceStopped(InCallServiceInfo info) {
+ maybeTrackMicrophoneUse(isMuted());
+ mCurrentUserInterfacePackageName = null;
+ String packageName = info.getComponentName().getPackageName();
+ Log.i(this, "trackCallingUserInterfaceStopped: %s is no longer calling UX", packageName);
+ }
+
+ /**
+ * As calls are added, removed and change between external and non-external status, track
+ * whether the current active calling UX is using the microphone. We assume if there is a
+ * managed call present and the mic is not muted that the microphone is in use.
+ */
+ private void maybeTrackMicrophoneUse(boolean isMuted) {
+ boolean wasTrackingManagedCall = mIsCallUsingMicrophone;
+ mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted;
+ if (wasTrackingManagedCall != mIsCallUsingMicrophone) {
+ if (mIsCallUsingMicrophone) {
+ mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+ mContext.getOpPackageName(), false, null, null);
+ } else {
+ mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+ mContext.getOpPackageName(), null);
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if InCallController is tracking a managed call (i.e. not self managed
+ * and not external) that is active.
+ */
+ private boolean isTrackingManagedAliveCall() {
+ return mCallIdMapper.getCalls().stream().anyMatch(c -> !c.isExternalCall()
+ && !c.isSelfManaged() && c.isAlive() && c.getState() != CallState.ON_HOLD
+ && c.getState() != CallState.AUDIO_PROCESSING);
+ }
+
+ /**
+ * @return {@code true} if the audio is currently muted, {@code false} otherwise.
+ */
+ private boolean isMuted() {
+ if (mCallsManager.getAudioState() == null) {
+ return false;
+ }
+ return mCallsManager.getAudioState().isMuted();
+ }
+
private void sendCrashedInCallServiceNotification(String packageName) {
PackageManager packageManager = mContext.getPackageManager();
CharSequence appName;
diff --git a/src/com/android/server/telecom/VideoProviderProxy.java b/src/com/android/server/telecom/VideoProviderProxy.java
index 364e0f4..df11403 100644
--- a/src/com/android/server/telecom/VideoProviderProxy.java
+++ b/src/com/android/server/telecom/VideoProviderProxy.java
@@ -55,6 +55,7 @@
*/
public interface Listener {
void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
+ void onSetCamera(Call call, String cameraId);
}
/**
@@ -346,6 +347,12 @@
return;
}
}
+
+ // Inform other Telecom components of the change in camera status.
+ for (Listener listener : mListeners) {
+ listener.onSetCamera(mCall, cameraId);
+ }
+
try {
mConectionServiceVideoProvider.setCamera(cameraId, callingPackage,
targetSdkVersion);
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index a36b6b9..693859b 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -61,6 +61,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
+import android.telecom.CallAudioState;
import android.telecom.InCallService;
import android.telecom.ParcelableCall;
import android.telecom.PhoneAccountHandle;
@@ -170,6 +171,8 @@
mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache,
mTimeoutsAdapter);
when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
+ when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
+ .thenReturn(mNotificationManager);
mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
mEmergencyCallHelper, mCarModeTracker, mClockProxy);
@@ -179,8 +182,6 @@
verify(mMockSystemStateHelper).addListener(systemStateListenerArgumentCaptor.capture());
mSystemStateListener = systemStateListenerArgumentCaptor.getValue();
- when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
- .thenReturn(mNotificationManager);
// Companion Apps don't have CONTROL_INCALL_EXPERIENCE permission.
doAnswer(invocation -> {
int uid = invocation.getArgument(0);
@@ -212,6 +213,7 @@
when(mMockPackageManager.checkPermission(
matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
}
@Override