Loading flags/telecom_api_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,13 @@ flag{ bug: "310669304" } flag{ name: "transactional_video_state" namespace: "telecom" description: "when set, clients using transactional implementations will be able to set & get the video state" bug: "311265260" } flag{ name: "business_call_composer" namespace: "telecom" Loading src/com/android/server/telecom/Call.java +47 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.server.telecom; import static android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED; import static android.telephony.TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE; import static com.android.server.telecom.voip.VideoStateTranslation.VideoProfileStateToTransactionalVideoState; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -651,6 +653,36 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, */ private boolean mIsVideoCallingSupportedByPhoneAccount = false; /** * Indicates whether this individual calls video state can be changed as opposed to be gated * by the {@link PhoneAccount}. * * {@code True} if the call is Transactional && has the CallAttributes.SUPPORTS_VIDEO_CALLING * capability {@code false} otherwise. */ private boolean mTransactionalCallSupportsVideoCalling = false; public void setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes) { if (!mIsTransactionalCall) { Log.i(this, "setTransactionalCallSupportsVideoCalling: call is not transactional"); return; } if (callAttributes == null) { Log.i(this, "setTransactionalCallSupportsVideoCalling: callAttributes is null"); return; } if ((callAttributes.getCallCapabilities() & CallAttributes.SUPPORTS_VIDEO_CALLING) == CallAttributes.SUPPORTS_VIDEO_CALLING) { mTransactionalCallSupportsVideoCalling = true; } else { mTransactionalCallSupportsVideoCalling = false; } } public boolean isTransactionalCallSupportsVideoCalling() { return mTransactionalCallSupportsVideoCalling; } /** * Indicates whether or not this call can be pulled if it is an external call. If true, respect * the Connection Capability set by the ConnectionService. If false, override the capability Loading Loading @@ -4022,6 +4054,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, videoState = VideoProfile.STATE_AUDIO_ONLY; } // Transactional calls have the ability to change video calling capabilities on a per-call // basis as opposed to ConnectionService calls which are only based on the PhoneAccount. if (mFlags.transactionalVideoState() && mIsTransactionalCall && !mTransactionalCallSupportsVideoCalling) { Log.i(this, "setVideoState: The transactional does NOT support video calling." + " defaulted to audio (video not supported)"); videoState = VideoProfile.STATE_AUDIO_ONLY; } // Track Video State history during the duration of the call. // Only update the history when the call is active or disconnected. This ensures we do // not include the video state history when: Loading @@ -4044,6 +4085,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } } if (mFlags.transactionalVideoState() && mIsTransactionalCall && mTransactionalService != null) { int transactionalVS = VideoProfileStateToTransactionalVideoState(mVideoState); mTransactionalService.onVideoStateChanged(this, transactionalVS); } if (VideoProfile.isVideo(videoState)) { mAnalytics.setCallIsVideo(true); } Loading src/com/android/server/telecom/TelecomServiceImpl.java +3 −0 Original line number Diff line number Diff line Loading @@ -239,6 +239,9 @@ public class TelecomServiceImpl { callEventCallback, mCallsManager, call); call.setTransactionServiceWrapper(serviceWrapper); if (mFeatureFlags.transactionalVideoState()) { call.setTransactionalCallSupportsVideoCalling(callAttributes); } ICallControl clientCallControl = serviceWrapper.getICallControl(); if (clientCallControl == null) { Loading src/com/android/server/telecom/TransactionalServiceWrapper.java +27 −1 Original line number Diff line number Diff line Loading @@ -43,10 +43,10 @@ import com.android.server.telecom.voip.EndpointChangeTransaction; import com.android.server.telecom.voip.HoldCallTransaction; import com.android.server.telecom.voip.EndCallTransaction; import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction; import com.android.server.telecom.voip.ParallelTransaction; import com.android.server.telecom.voip.RequestNewActiveCallTransaction; import com.android.server.telecom.voip.SerialTransaction; import com.android.server.telecom.voip.SetMuteStateTransaction; import com.android.server.telecom.voip.RequestVideoStateTransaction; import com.android.server.telecom.voip.TransactionManager; import com.android.server.telecom.voip.VoipCallTransaction; import com.android.server.telecom.voip.VoipCallTransactionResult; Loading @@ -71,6 +71,7 @@ public class TransactionalServiceWrapper implements public static final String ANSWER = "Answer"; public static final String DISCONNECT = "Disconnect"; public static final String START_STREAMING = "StartStreaming"; public static final String REQUEST_VIDEO_STATE = "RequestVideoState"; // CallEventCallback : Telecom --> Client (ex. voip app) public static final String ON_SET_ACTIVE = "onSetActive"; Loading Loading @@ -248,6 +249,17 @@ public class TransactionalServiceWrapper implements } } @Override public void requestVideoState(int videoState, String callId, ResultReceiver callback) throws RemoteException { try { Log.startSession("TSW.rVS"); createTransactions(callId, callback, REQUEST_VIDEO_STATE, videoState); } finally { Log.endSession(); } } private void createTransactions(String callId, ResultReceiver callback, String action, Object... objects) { Log.d(TAG, "createTransactions: callId=" + callId); Loading @@ -274,6 +286,11 @@ public class TransactionalServiceWrapper implements addTransactionsToManager(mStreamingController.getStartStreamingTransaction(mCallsManager, TransactionalServiceWrapper.this, call, mLock), callback); break; case REQUEST_VIDEO_STATE: addTransactionsToManager( new RequestVideoStateTransaction(mCallsManager, call, (int) objects[0]), callback); break; } } else { Bundle exceptionBundle = new Bundle(); Loading Loading @@ -562,6 +579,15 @@ public class TransactionalServiceWrapper implements } } public void onVideoStateChanged(Call call, int videoState) { if (call != null) { try { mICallEventCallback.onVideoStateChanged(call.getId(), videoState); } catch (RemoteException e) { } } } public void removeCallFromWrappers(Call call) { if (call != null) { try { Loading src/com/android/server/telecom/voip/RequestVideoStateTransaction.java 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ package com.android.server.telecom.voip; import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState; import android.telecom.VideoProfile; import android.util.Log; import com.android.server.telecom.CallsManager; import com.android.server.telecom.Call; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; public class RequestVideoStateTransaction extends VoipCallTransaction { private static final String TAG = RequestVideoStateTransaction.class.getSimpleName(); private final Call mCall; private final int mVideoProfileState; public RequestVideoStateTransaction(CallsManager callsManager, Call call, int transactionalVideoState) { super(callsManager.getLock()); mCall = call; mVideoProfileState = TransactionalVideoStateToVideoProfileState(transactionalVideoState); } @Override public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { Log.d(TAG, "processTransaction"); CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); if (isRequestingVideoTransmission(mVideoProfileState) && !mCall.isVideoCallingSupportedByPhoneAccount()) { future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, "Video calling is not supported by the target account")); } else if (isRequestingVideoTransmission(mVideoProfileState) && !mCall.isTransactionalCallSupportsVideoCalling()) { future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, "Video calling is not supported according to the callAttributes")); } else { mCall.setVideoState(mVideoProfileState); future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_SUCCEED, "The Video State was changed successfully")); } return future; } private boolean isRequestingVideoTransmission(int targetVideoState) { return targetVideoState != VideoProfile.STATE_AUDIO_ONLY; } } No newline at end of file Loading
flags/telecom_api_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,13 @@ flag{ bug: "310669304" } flag{ name: "transactional_video_state" namespace: "telecom" description: "when set, clients using transactional implementations will be able to set & get the video state" bug: "311265260" } flag{ name: "business_call_composer" namespace: "telecom" Loading
src/com/android/server/telecom/Call.java +47 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.server.telecom; import static android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED; import static android.telephony.TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE; import static com.android.server.telecom.voip.VideoStateTranslation.VideoProfileStateToTransactionalVideoState; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -651,6 +653,36 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, */ private boolean mIsVideoCallingSupportedByPhoneAccount = false; /** * Indicates whether this individual calls video state can be changed as opposed to be gated * by the {@link PhoneAccount}. * * {@code True} if the call is Transactional && has the CallAttributes.SUPPORTS_VIDEO_CALLING * capability {@code false} otherwise. */ private boolean mTransactionalCallSupportsVideoCalling = false; public void setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes) { if (!mIsTransactionalCall) { Log.i(this, "setTransactionalCallSupportsVideoCalling: call is not transactional"); return; } if (callAttributes == null) { Log.i(this, "setTransactionalCallSupportsVideoCalling: callAttributes is null"); return; } if ((callAttributes.getCallCapabilities() & CallAttributes.SUPPORTS_VIDEO_CALLING) == CallAttributes.SUPPORTS_VIDEO_CALLING) { mTransactionalCallSupportsVideoCalling = true; } else { mTransactionalCallSupportsVideoCalling = false; } } public boolean isTransactionalCallSupportsVideoCalling() { return mTransactionalCallSupportsVideoCalling; } /** * Indicates whether or not this call can be pulled if it is an external call. If true, respect * the Connection Capability set by the ConnectionService. If false, override the capability Loading Loading @@ -4022,6 +4054,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, videoState = VideoProfile.STATE_AUDIO_ONLY; } // Transactional calls have the ability to change video calling capabilities on a per-call // basis as opposed to ConnectionService calls which are only based on the PhoneAccount. if (mFlags.transactionalVideoState() && mIsTransactionalCall && !mTransactionalCallSupportsVideoCalling) { Log.i(this, "setVideoState: The transactional does NOT support video calling." + " defaulted to audio (video not supported)"); videoState = VideoProfile.STATE_AUDIO_ONLY; } // Track Video State history during the duration of the call. // Only update the history when the call is active or disconnected. This ensures we do // not include the video state history when: Loading @@ -4044,6 +4085,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } } if (mFlags.transactionalVideoState() && mIsTransactionalCall && mTransactionalService != null) { int transactionalVS = VideoProfileStateToTransactionalVideoState(mVideoState); mTransactionalService.onVideoStateChanged(this, transactionalVS); } if (VideoProfile.isVideo(videoState)) { mAnalytics.setCallIsVideo(true); } Loading
src/com/android/server/telecom/TelecomServiceImpl.java +3 −0 Original line number Diff line number Diff line Loading @@ -239,6 +239,9 @@ public class TelecomServiceImpl { callEventCallback, mCallsManager, call); call.setTransactionServiceWrapper(serviceWrapper); if (mFeatureFlags.transactionalVideoState()) { call.setTransactionalCallSupportsVideoCalling(callAttributes); } ICallControl clientCallControl = serviceWrapper.getICallControl(); if (clientCallControl == null) { Loading
src/com/android/server/telecom/TransactionalServiceWrapper.java +27 −1 Original line number Diff line number Diff line Loading @@ -43,10 +43,10 @@ import com.android.server.telecom.voip.EndpointChangeTransaction; import com.android.server.telecom.voip.HoldCallTransaction; import com.android.server.telecom.voip.EndCallTransaction; import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction; import com.android.server.telecom.voip.ParallelTransaction; import com.android.server.telecom.voip.RequestNewActiveCallTransaction; import com.android.server.telecom.voip.SerialTransaction; import com.android.server.telecom.voip.SetMuteStateTransaction; import com.android.server.telecom.voip.RequestVideoStateTransaction; import com.android.server.telecom.voip.TransactionManager; import com.android.server.telecom.voip.VoipCallTransaction; import com.android.server.telecom.voip.VoipCallTransactionResult; Loading @@ -71,6 +71,7 @@ public class TransactionalServiceWrapper implements public static final String ANSWER = "Answer"; public static final String DISCONNECT = "Disconnect"; public static final String START_STREAMING = "StartStreaming"; public static final String REQUEST_VIDEO_STATE = "RequestVideoState"; // CallEventCallback : Telecom --> Client (ex. voip app) public static final String ON_SET_ACTIVE = "onSetActive"; Loading Loading @@ -248,6 +249,17 @@ public class TransactionalServiceWrapper implements } } @Override public void requestVideoState(int videoState, String callId, ResultReceiver callback) throws RemoteException { try { Log.startSession("TSW.rVS"); createTransactions(callId, callback, REQUEST_VIDEO_STATE, videoState); } finally { Log.endSession(); } } private void createTransactions(String callId, ResultReceiver callback, String action, Object... objects) { Log.d(TAG, "createTransactions: callId=" + callId); Loading @@ -274,6 +286,11 @@ public class TransactionalServiceWrapper implements addTransactionsToManager(mStreamingController.getStartStreamingTransaction(mCallsManager, TransactionalServiceWrapper.this, call, mLock), callback); break; case REQUEST_VIDEO_STATE: addTransactionsToManager( new RequestVideoStateTransaction(mCallsManager, call, (int) objects[0]), callback); break; } } else { Bundle exceptionBundle = new Bundle(); Loading Loading @@ -562,6 +579,15 @@ public class TransactionalServiceWrapper implements } } public void onVideoStateChanged(Call call, int videoState) { if (call != null) { try { mICallEventCallback.onVideoStateChanged(call.getId(), videoState); } catch (RemoteException e) { } } } public void removeCallFromWrappers(Call call) { if (call != null) { try { Loading
src/com/android/server/telecom/voip/RequestVideoStateTransaction.java 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ package com.android.server.telecom.voip; import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState; import android.telecom.VideoProfile; import android.util.Log; import com.android.server.telecom.CallsManager; import com.android.server.telecom.Call; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; public class RequestVideoStateTransaction extends VoipCallTransaction { private static final String TAG = RequestVideoStateTransaction.class.getSimpleName(); private final Call mCall; private final int mVideoProfileState; public RequestVideoStateTransaction(CallsManager callsManager, Call call, int transactionalVideoState) { super(callsManager.getLock()); mCall = call; mVideoProfileState = TransactionalVideoStateToVideoProfileState(transactionalVideoState); } @Override public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { Log.d(TAG, "processTransaction"); CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); if (isRequestingVideoTransmission(mVideoProfileState) && !mCall.isVideoCallingSupportedByPhoneAccount()) { future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, "Video calling is not supported by the target account")); } else if (isRequestingVideoTransmission(mVideoProfileState) && !mCall.isTransactionalCallSupportsVideoCalling()) { future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_FAILED, "Video calling is not supported according to the callAttributes")); } else { mCall.setVideoState(mVideoProfileState); future.complete(new VoipCallTransactionResult( VoipCallTransactionResult.RESULT_SUCCEED, "The Video State was changed successfully")); } return future; } private boolean isRequestingVideoTransmission(int targetVideoState) { return targetVideoState != VideoProfile.STATE_AUDIO_ONLY; } } No newline at end of file