• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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