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.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.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;
25 
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.os.Bundle;
30 import android.telecom.CallAttributes;
31 import android.telecom.TelecomManager;
32 import android.util.Log;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.server.telecom.Call;
36 import com.android.server.telecom.CallsManager;
37 import com.android.server.telecom.LoggedHandlerExecutor;
38 import com.android.server.telecom.flags.FeatureFlags;
39 
40 import java.util.concurrent.CompletableFuture;
41 import java.util.concurrent.CompletionStage;
42 
43 public class OutgoingCallTransaction extends VoipCallTransaction {
44 
45     private static final String TAG = OutgoingCallTransaction.class.getSimpleName();
46     private final String mCallId;
47     private final Context mContext;
48     private final String mCallingPackage;
49     private final CallAttributes mCallAttributes;
50     private final CallsManager mCallsManager;
51     private final Bundle mExtras;
52     private FeatureFlags mFeatureFlags;
53 
setFeatureFlags(FeatureFlags featureFlags)54     public void setFeatureFlags(FeatureFlags featureFlags) {
55         mFeatureFlags = featureFlags;
56     }
57 
OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, CallsManager callsManager, Bundle extras, FeatureFlags featureFlags)58     public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
59             CallsManager callsManager, Bundle extras, FeatureFlags featureFlags) {
60         super(callsManager.getLock());
61         mCallId = callId;
62         mContext = context;
63         mCallAttributes = callAttributes;
64         mCallsManager = callsManager;
65         mExtras = extras;
66         mCallingPackage = mContext.getOpPackageName();
67         mFeatureFlags = featureFlags;
68     }
69 
OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes, CallsManager callsManager, FeatureFlags featureFlags)70     public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
71             CallsManager callsManager, FeatureFlags featureFlags) {
72         this(callId, context, callAttributes, callsManager, new Bundle(), featureFlags);
73     }
74 
75     @Override
processTransaction(Void v)76     public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
77         Log.d(TAG, "processTransaction");
78 
79         final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
80                 CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;
81 
82         final Intent intent = new Intent(hasCallPrivilegedPermission ?
83                 Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, mCallAttributes.getAddress());
84 
85         if (mCallsManager.isOutgoingCallPermitted(mCallAttributes.getPhoneAccountHandle())) {
86             Log.d(TAG, "processTransaction: outgoing call permitted");
87 
88             CompletableFuture<Call> callFuture =
89                     mCallsManager.startOutgoingCall(mCallAttributes.getAddress(),
90                             mCallAttributes.getPhoneAccountHandle(),
91                             generateExtras(mCallAttributes),
92                             mCallAttributes.getPhoneAccountHandle().getUserHandle(),
93                             intent,
94                             mCallingPackage);
95 
96             if (callFuture == null) {
97                 return CompletableFuture.completedFuture(
98                         new VoipCallTransactionResult(
99                                 CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
100                                 "incoming call not permitted at the current time"));
101             }
102             CompletionStage<VoipCallTransactionResult> result = callFuture.thenComposeAsync(
103                     (call) -> {
104 
105                         Log.d(TAG, "processTransaction: completing future");
106 
107                         if (call == null) {
108                             Log.d(TAG, "processTransaction: call is null");
109                             return CompletableFuture.completedFuture(
110                                     new VoipCallTransactionResult(
111                                             CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
112                                             "call could not be created at this time"));
113                         } else {
114                             Log.d(TAG, "processTransaction: call done. id=" + call.getId());
115                         }
116 
117                         return CompletableFuture.completedFuture(
118                                 new VoipCallTransactionResult(
119                                         VoipCallTransactionResult.RESULT_SUCCEED,
120                                         call, null));
121                     }
122                     , new LoggedHandlerExecutor(mHandler, "OCT.pT", null));
123 
124             return result;
125         } else {
126             return CompletableFuture.completedFuture(
127                     new VoipCallTransactionResult(
128                             CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
129                             "incoming call not permitted at the current time"));
130 
131         }
132     }
133 
134     @VisibleForTesting
generateExtras(CallAttributes callAttributes)135     public Bundle generateExtras(CallAttributes callAttributes) {
136         mExtras.setDefusable(true);
137         mExtras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
138         mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
139         if (mFeatureFlags.transactionalVideoState()) {
140             // Transactional calls need to remap the CallAttributes video state to the existing
141             // VideoProfile for consistency.
142             mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
143                     TransactionalVideoStateToVideoProfileState(callAttributes.getCallType()));
144         } else {
145             mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
146                     callAttributes.getCallType());
147         }
148         mExtras.putCharSequence(DISPLAY_NAME_KEY, callAttributes.getDisplayName());
149         return mExtras;
150     }
151 }
152