1 /* 2 * Copyright (C) 2015 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 package com.android.car.dialer.telecom; 17 18 import android.bluetooth.BluetoothDevice; 19 import android.content.Intent; 20 import android.os.Binder; 21 import android.os.IBinder; 22 import android.os.Process; 23 import android.telecom.Call; 24 import android.telecom.CallAudioState; 25 import android.telecom.InCallService; 26 import android.telecom.PhoneAccountHandle; 27 28 import androidx.lifecycle.LiveData; 29 30 import com.android.car.dialer.bluetooth.PhoneAccountManager; 31 import com.android.car.dialer.framework.InCallServiceProxy; 32 import com.android.car.dialer.log.L; 33 34 import java.util.concurrent.CopyOnWriteArrayList; 35 36 import javax.inject.Inject; 37 import javax.inject.Named; 38 39 import dagger.hilt.android.AndroidEntryPoint; 40 41 /** 42 * An implementation of {@link InCallService}. This service is bounded by android telecom and 43 * {@link UiCallManager}. For incoming calls it will launch Dialer app. 44 */ 45 @AndroidEntryPoint(InCallServiceProxy.class) 46 public class InCallServiceImpl extends Hilt_InCallServiceImpl { 47 private static final String TAG = "CD.InCallService"; 48 49 /** An action which indicates a bind is from local component. */ 50 public static final String ACTION_LOCAL_BIND = "local_bind"; 51 52 private CopyOnWriteArrayList<CallAudioStateCallback> mCallAudioStateCallbacks = 53 new CopyOnWriteArrayList<>(); 54 55 @Inject InCallRouter mInCallRouter; 56 @Inject PhoneAccountManager mPhoneAccountManager; 57 @Inject @Named("Hfp") LiveData<BluetoothDevice> mCurrentHfpDeviceLiveData; 58 59 /** Listens to active call list changes. Callbacks will be called on main thread. */ 60 public interface ActiveCallListChangedCallback { 61 62 /** 63 * Called when a new call is added. 64 * 65 * @return if the given call has been handled by this callback. 66 */ onTelecomCallAdded(Call telecomCall)67 boolean onTelecomCallAdded(Call telecomCall); 68 69 /** 70 * Called when an existing call is removed. 71 * 72 * @return if the given call has been handled by this callback. 73 */ onTelecomCallRemoved(Call telecomCall)74 boolean onTelecomCallRemoved(Call telecomCall); 75 } 76 77 /** Listens to call audio state changes. Callbacks will be called on the main thread. */ 78 public interface CallAudioStateCallback { 79 /** 80 * Called when the call audio state has changed. 81 */ onCallAudioStateChanged(CallAudioState callAudioState)82 void onCallAudioStateChanged(CallAudioState callAudioState); 83 } 84 85 @Override onCreate()86 public void onCreate() { 87 super.onCreate(); 88 mInCallRouter.start(); 89 } 90 91 @Override onDestroy()92 public void onDestroy() { 93 super.onDestroy(); 94 mInCallRouter.stop(); 95 } 96 97 @Override onCallAdded(Call telecomCall)98 public void onCallAdded(Call telecomCall) { 99 L.d(TAG, "onCallAdded: %s", telecomCall); 100 if (telecomCall.getState() == Call.STATE_SELECT_PHONE_ACCOUNT) { 101 BluetoothDevice currentHfpDevice = mCurrentHfpDeviceLiveData.getValue(); 102 PhoneAccountHandle currentPhoneAccountHandle = 103 mPhoneAccountManager.getMatchingPhoneAccount(currentHfpDevice); 104 if (currentPhoneAccountHandle != null) { 105 telecomCall.phoneAccountSelected(currentPhoneAccountHandle, false); 106 } else { 107 L.e(TAG, "Not able to get the phone account handle for current hfp device."); 108 } 109 } 110 mInCallRouter.onCallAdded(telecomCall); 111 } 112 113 @Override onCallRemoved(Call telecomCall)114 public void onCallRemoved(Call telecomCall) { 115 L.d(TAG, "onCallRemoved: %s", telecomCall); 116 mInCallRouter.onCallRemoved(telecomCall); 117 } 118 119 @Override onBind(Intent intent)120 public IBinder onBind(Intent intent) { 121 L.d(TAG, "onBind: %s", intent); 122 return ACTION_LOCAL_BIND.equals(intent.getAction()) 123 ? new LocalBinder() 124 : super.onBind(intent); 125 } 126 127 @Override onUnbind(Intent intent)128 public boolean onUnbind(Intent intent) { 129 L.d(TAG, "onUnbind, intent: %s", intent); 130 if (ACTION_LOCAL_BIND.equals(intent.getAction())) { 131 return false; 132 } 133 return super.onUnbind(intent); 134 } 135 136 @Override onCallAudioStateChanged(CallAudioState audioState)137 public void onCallAudioStateChanged(CallAudioState audioState) { 138 for (CallAudioStateCallback callback : mCallAudioStateCallbacks) { 139 callback.onCallAudioStateChanged(audioState); 140 } 141 } 142 143 @Override onBringToForeground(boolean showDialpad)144 public void onBringToForeground(boolean showDialpad) { 145 L.d(TAG, "onBringToForeground: %s", showDialpad); 146 mInCallRouter.routeToFullScreenIncomingCallPage(showDialpad); 147 } 148 addCallAudioStateChangedCallback(CallAudioStateCallback callback)149 public void addCallAudioStateChangedCallback(CallAudioStateCallback callback) { 150 mCallAudioStateCallbacks.add(callback); 151 } 152 removeCallAudioStateChangedCallback(CallAudioStateCallback callback)153 public void removeCallAudioStateChangedCallback(CallAudioStateCallback callback) { 154 mCallAudioStateCallbacks.remove(callback); 155 } 156 addActiveCallListChangedCallback(ActiveCallListChangedCallback callback)157 public void addActiveCallListChangedCallback(ActiveCallListChangedCallback callback) { 158 mInCallRouter.registerActiveCallListChangedCallback(callback); 159 } 160 removeActiveCallListChangedCallback(ActiveCallListChangedCallback callback)161 public void removeActiveCallListChangedCallback(ActiveCallListChangedCallback callback) { 162 mInCallRouter.unregisterActiveCallHandler(callback); 163 } 164 165 /** 166 * Local binder only available for Car Dialer package. 167 */ 168 public class LocalBinder extends Binder { 169 170 /** 171 * Returns a reference to {@link InCallServiceImpl}. Any process other than Dialer 172 * process won't be able to get a reference. 173 */ getService()174 public InCallServiceImpl getService() { 175 if (getCallingPid() == Process.myPid()) { 176 return InCallServiceImpl.this; 177 } 178 return null; 179 } 180 } 181 } 182