1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom.callsequencing.voip; 18 19 import static android.Manifest.permission.CALL_PRIVILEGED; 20 import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY; 21 import static android.telecom.CallAttributes.DISPLAY_NAME_KEY; 22 import static android.telecom.CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME; 23 24 import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation 25 .TransactionalVideoStateToVideoProfileState; 26 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.PackageManager; 30 import android.os.Bundle; 31 import android.telecom.CallAttributes; 32 import android.telecom.TelecomManager; 33 import android.util.Log; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.server.telecom.Call; 37 import com.android.server.telecom.CallsManager; 38 import com.android.server.telecom.LoggedHandlerExecutor; 39 import com.android.server.telecom.callsequencing.CallTransaction; 40 import com.android.server.telecom.callsequencing.CallTransactionResult; 41 import com.android.server.telecom.flags.FeatureFlags; 42 43 import java.util.concurrent.CompletableFuture; 44 import java.util.concurrent.CompletionStage; 45 46 public class OutgoingCallTransaction extends CallTransaction { 47 48 private static final String TAG = OutgoingCallTransaction.class.getSimpleName(); 49 private final String mCallId; 50 private final Context mContext; 51 private final String mCallingPackage; 52 private final CallAttributes mCallAttributes; 53 private final CallsManager mCallsManager; 54 private final Bundle mExtras; 55 private FeatureFlags mFeatureFlags; 56 setFeatureFlags(FeatureFlags featureFlags)57 public void setFeatureFlags(FeatureFlags featureFlags) { 58 mFeatureFlags = featureFlags; 59 } 60 OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, CallsManager callsManager, Bundle extras, FeatureFlags featureFlags)61 public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, 62 CallsManager callsManager, Bundle extras, FeatureFlags featureFlags) { 63 super(callsManager.getLock()); 64 mCallId = callId; 65 mContext = context; 66 mCallAttributes = callAttributes; 67 mCallsManager = callsManager; 68 mExtras = extras; 69 mCallingPackage = mContext.getOpPackageName(); 70 mFeatureFlags = featureFlags; 71 } 72 OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, CallsManager callsManager, FeatureFlags featureFlags)73 public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, 74 CallsManager callsManager, FeatureFlags featureFlags) { 75 this(callId, context, callAttributes, callsManager, new Bundle(), featureFlags); 76 } 77 78 @Override processTransaction(Void v)79 public CompletionStage<CallTransactionResult> processTransaction(Void v) { 80 Log.d(TAG, "processTransaction"); 81 82 final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission( 83 CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED; 84 85 final Intent intent = new Intent(hasCallPrivilegedPermission ? 86 Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, mCallAttributes.getAddress()); 87 88 if (mCallsManager.isOutgoingCallPermitted(mCallAttributes.getPhoneAccountHandle())) { 89 Log.d(TAG, "processTransaction: outgoing call permitted"); 90 91 CompletableFuture<Call> callFuture = 92 mCallsManager.startOutgoingCall(mCallAttributes.getAddress(), 93 mCallAttributes.getPhoneAccountHandle(), 94 generateExtras(mCallId, mExtras, mCallAttributes, mFeatureFlags), 95 mCallAttributes.getPhoneAccountHandle().getUserHandle(), 96 intent, 97 mCallingPackage); 98 99 if (callFuture == null) { 100 return CompletableFuture.completedFuture( 101 new CallTransactionResult( 102 CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME, 103 "incoming call not permitted at the current time")); 104 } 105 106 return callFuture.thenComposeAsync( 107 (call) -> processOutgoingCallTransactionHelper(call, TAG, 108 mCallsManager, mFeatureFlags) 109 , new LoggedHandlerExecutor(mHandler, "OCT.pT", null)); 110 } else { 111 return CompletableFuture.completedFuture( 112 new CallTransactionResult( 113 CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME, 114 "incoming call not permitted at the current time")); 115 116 } 117 } 118 119 @VisibleForTesting generateExtras(String callId, Bundle extras, CallAttributes callAttributes, FeatureFlags featureFlags)120 public static Bundle generateExtras(String callId, Bundle extras, 121 CallAttributes callAttributes, FeatureFlags featureFlags) { 122 extras.setDefusable(true); 123 extras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, callId); 124 extras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities()); 125 if (featureFlags.transactionalVideoState()) { 126 // Transactional calls need to remap the CallAttributes video state to the existing 127 // VideoProfile for consistency. 128 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 129 TransactionalVideoStateToVideoProfileState(callAttributes.getCallType())); 130 } else { 131 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 132 callAttributes.getCallType()); 133 } 134 extras.putCharSequence(DISPLAY_NAME_KEY, callAttributes.getDisplayName()); 135 return extras; 136 } 137 processOutgoingCallTransactionHelper( Call call, String tag, CallsManager callsManager, FeatureFlags featureFlags)138 public static CompletableFuture<CallTransactionResult> processOutgoingCallTransactionHelper( 139 Call call, String tag, CallsManager callsManager, FeatureFlags featureFlags) { 140 Log.d(tag, "processTransaction: completing future"); 141 142 if (call == null) { 143 Log.d(tag, "processTransaction: call is null"); 144 return CompletableFuture.completedFuture( 145 new CallTransactionResult( 146 CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME, 147 "call could not be created at this time")); 148 } else { 149 Log.d(tag, "processTransaction: call done. id=" + call.getId()); 150 } 151 152 if (featureFlags.disconnectSelfManagedStuckStartupCalls()) { 153 // set to dialing so the CallAnomalyWatchdog gives the VoIP calls 1 154 // minute to timeout rather than 5 seconds. 155 callsManager.markCallAsDialing(call); 156 } 157 158 return CompletableFuture.completedFuture( 159 new CallTransactionResult( 160 CallTransactionResult.RESULT_SUCCEED, 161 call, null, true)); 162 } 163 } 164