| /* |
| * Copyright (C) 2021 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; |
| |
| import android.Manifest; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.RequiresPermission; |
| import android.annotation.UserIdInt; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.telecom.BluetoothCallQualityReport; |
| import android.telecom.CallAudioState; |
| import android.telecom.CallDiagnosticService; |
| import android.telecom.ConnectionService; |
| import android.telecom.CallDiagnostics; |
| import android.telecom.DisconnectCause; |
| import android.telecom.InCallService; |
| import android.telecom.Log; |
| import android.telecom.ParcelableCall; |
| import android.telephony.CallQuality; |
| import android.telephony.ims.ImsReasonInfo; |
| import android.text.TextUtils; |
| |
| import com.android.internal.telecom.ICallDiagnosticService; |
| import com.android.internal.util.IndentingPrintWriter; |
| |
| import java.util.List; |
| |
| /** |
| * Responsible for maintaining binding to the {@link CallDiagnosticService} defined by the |
| * {@code call_diagnostic_service_package_name} key in the |
| * {@code packages/services/Telecomm/res/values/config.xml} file. |
| */ |
| public class CallDiagnosticServiceController extends CallsManagerListenerBase { |
| /** |
| * Context dependencies for the {@link CallDiagnosticServiceController}. |
| */ |
| public interface ContextProxy { |
| List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent, |
| @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId); |
| boolean bindServiceAsUser(@NonNull @RequiresPermission Intent service, |
| @NonNull ServiceConnection conn, int flags, @NonNull UserHandle user); |
| void unbindService(@NonNull ServiceConnection conn); |
| UserHandle getCurrentUserHandle(); |
| } |
| |
| /** |
| * Listener for {@link Call} events; used to propagate these changes to the |
| * {@link CallDiagnosticService}. |
| */ |
| private final Call.Listener mCallListener = new Call.ListenerBase() { |
| @Override |
| public void onConnectionCapabilitiesChanged(Call call) { |
| updateCall(call); |
| } |
| |
| @Override |
| public void onConnectionPropertiesChanged(Call call, boolean didRttChange) { |
| updateCall(call); |
| } |
| |
| /** |
| * Listens for changes to extras reported by a Telecom {@link Call}. |
| * |
| * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService} |
| * so we will only trigger an update of the call information if the source of the extras |
| * change was a {@link ConnectionService}. |
| * |
| * @param call The call. |
| * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or |
| * {@link Call#SOURCE_INCALL_SERVICE}). |
| * @param extras The extras. |
| */ |
| @Override |
| public void onExtrasChanged(Call call, int source, Bundle extras) { |
| // Do not inform InCallServices of changes which originated there. |
| if (source == Call.SOURCE_INCALL_SERVICE) { |
| return; |
| } |
| updateCall(call); |
| } |
| |
| /** |
| * Listens for changes to extras reported by a Telecom {@link Call}. |
| * |
| * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService} |
| * so we will only trigger an update of the call information if the source of the extras |
| * change was a {@link ConnectionService}. |
| * @param call The call. |
| * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or |
| * {@link Call#SOURCE_INCALL_SERVICE}). |
| * @param keys The extra key removed |
| */ |
| @Override |
| public void onExtrasRemoved(Call call, int source, List<String> keys) { |
| // Do not inform InCallServices of changes which originated there. |
| if (source == Call.SOURCE_INCALL_SERVICE) { |
| return; |
| } |
| updateCall(call); |
| } |
| |
| /** |
| * Handles changes to the video state of a call. |
| * @param call |
| * @param previousVideoState |
| * @param newVideoState |
| */ |
| @Override |
| public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { |
| updateCall(call); |
| } |
| |
| /** |
| * Relays a bluetooth call quality report received from the Bluetooth stack to the |
| * CallDiagnosticService. |
| * @param call The call. |
| * @param report The received report. |
| */ |
| @Override |
| public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) { |
| handleBluetoothCallQualityReport(call, report); |
| } |
| |
| /** |
| * Relays a device to device message received from Telephony to the CallDiagnosticService. |
| * @param call |
| * @param messageType |
| * @param messageValue |
| */ |
| @Override |
| public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) { |
| handleReceivedDeviceToDeviceMessage(call, messageType, messageValue); |
| } |
| |
| /** |
| * Handles an incoming {@link CallQuality} report from a {@link android.telecom.Connection}. |
| * @param call The call. |
| * @param callQualityReport The call quality report. |
| */ |
| @Override |
| public void onReceivedCallQualityReport(Call call, CallQuality callQualityReport) { |
| handleCallQualityReport(call, callQualityReport); |
| } |
| }; |
| |
| /** |
| * {@link ServiceConnection} handling changes to binding of the {@link CallDiagnosticService}. |
| */ |
| private class CallDiagnosticServiceConnection implements ServiceConnection { |
| @Override |
| public void onServiceConnected(ComponentName name, IBinder service) { |
| Log.startSession("CDSC.oSC", Log.getPackageAbbreviation(name)); |
| try { |
| synchronized (mLock) { |
| mCallDiagnosticService = ICallDiagnosticService.Stub.asInterface(service); |
| |
| handleConnectionComplete(mCallDiagnosticService); |
| } |
| Log.i(CallDiagnosticServiceController.this, "onServiceConnected: cmp=%s", name); |
| } finally { |
| Log.endSession(); |
| } |
| } |
| |
| @Override |
| public void onServiceDisconnected(ComponentName name) { |
| Log.startSession("CDSC.oSD", Log.getPackageAbbreviation(name)); |
| try { |
| synchronized (mLock) { |
| mCallDiagnosticService = null; |
| mConnection = null; |
| } |
| Log.i(CallDiagnosticServiceController.this, "onServiceDisconnected: cmp=%s", name); |
| } finally { |
| Log.endSession(); |
| } |
| } |
| |
| @Override |
| public void onBindingDied(ComponentName name) { |
| Log.startSession("CDSC.oBD", Log.getPackageAbbreviation(name)); |
| try { |
| synchronized (mLock) { |
| mCallDiagnosticService = null; |
| mConnection = null; |
| } |
| Log.w(CallDiagnosticServiceController.this, "onBindingDied: cmp=%s", name); |
| } finally { |
| Log.endSession(); |
| } |
| } |
| |
| @Override |
| public void onNullBinding(ComponentName name) { |
| Log.startSession("CDSC.oNB", Log.getPackageAbbreviation(name)); |
| try { |
| synchronized (mLock) { |
| maybeUnbindCallScreeningService(); |
| } |
| } finally { |
| Log.endSession(); |
| } |
| } |
| } |
| |
| private final String mPackageName; |
| private final ContextProxy mContextProxy; |
| private InCallTonePlayer.Factory mPlayerFactory; |
| private String mTestPackageName; |
| private CallDiagnosticServiceConnection mConnection; |
| private CallDiagnosticServiceAdapter mAdapter; |
| private final TelecomSystem.SyncRoot mLock; |
| private ICallDiagnosticService mCallDiagnosticService; |
| private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId); |
| |
| public CallDiagnosticServiceController(@NonNull ContextProxy contextProxy, |
| @Nullable String packageName, @NonNull TelecomSystem.SyncRoot lock) { |
| mContextProxy = contextProxy; |
| mPackageName = packageName; |
| mLock = lock; |
| } |
| |
| /** |
| * Sets the current {@link InCallTonePlayer.Factory} for this instance. |
| * @param factory the factory. |
| */ |
| public void setInCallTonePlayerFactory(InCallTonePlayer.Factory factory) { |
| mPlayerFactory = factory; |
| } |
| |
| /** |
| * Handles Telecom adding new calls. Will bind to the call diagnostic service if needed and |
| * send the calls, or send to an already bound service. |
| * @param call The call to add. |
| */ |
| @Override |
| public void onCallAdded(@NonNull Call call) { |
| if (!call.isSimCall() || call.isExternalCall()) { |
| Log.i(this, "onCallAdded: skipping call %s as non-sim or external.", call.getId()); |
| return; |
| } |
| if (mCallIdMapper.getCallId(call) == null) { |
| mCallIdMapper.addCall(call); |
| call.addListener(mCallListener); |
| } |
| if (isConnected()) { |
| sendCallToBoundService(call, mCallDiagnosticService); |
| } else { |
| maybeBindCallDiagnosticService(); |
| } |
| } |
| |
| /** |
| * Handles a newly disconnected call signalled from {@link CallsManager}. |
| * @param call The call |
| * @param disconnectCause The disconnect cause |
| * @return {@code true} if the {@link CallDiagnosticService} was sent the call, {@code false} |
| * if the call was not applicable to the CDS or if there was an issue sending it. |
| */ |
| public boolean onCallDisconnected(@NonNull Call call, |
| @NonNull DisconnectCause disconnectCause) { |
| if (!call.isSimCall() || call.isExternalCall()) { |
| Log.i(this, "onCallDisconnected: skipping call %s as non-sim or external.", |
| call.getId()); |
| return false; |
| } |
| String callId = mCallIdMapper.getCallId(call); |
| try { |
| if (isConnected()) { |
| mCallDiagnosticService.notifyCallDisconnected(callId, disconnectCause); |
| return true; |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "onCallDisconnected: callId=%s, exception=%s", call.getId(), e); |
| } |
| return false; |
| } |
| |
| /** |
| * Handles Telecom removal of calls; will remove the call from the bound service and if the |
| * number of tracked calls falls to zero, unbind from the service. |
| * @param call The call to remove from the bound CDS. |
| */ |
| @Override |
| public void onCallRemoved(@NonNull Call call) { |
| if (!call.isSimCall() || call.isExternalCall()) { |
| Log.i(this, "onCallRemoved: skipping call %s as non-sim or external.", call.getId()); |
| return; |
| } |
| mCallIdMapper.removeCall(call); |
| call.removeListener(mCallListener); |
| removeCallFromBoundService(call, mCallDiagnosticService); |
| |
| if (mCallIdMapper.getCalls().size() == 0) { |
| maybeUnbindCallScreeningService(); |
| } |
| } |
| |
| @Override |
| public void onCallStateChanged(Call call, int oldState, int newState) { |
| updateCall(call); |
| } |
| |
| @Override |
| public void onCallAudioStateChanged(CallAudioState oldCallAudioState, |
| CallAudioState newCallAudioState) { |
| if (mCallDiagnosticService != null) { |
| try { |
| mCallDiagnosticService.updateCallAudioState(newCallAudioState); |
| } catch (RemoteException e) { |
| Log.w(this, "onCallAudioStateChanged: failed %s", e); |
| } |
| } |
| } |
| |
| /** |
| * Sets the test call diagnostic service; used by the telecom command line command to override |
| * the {@link CallDiagnosticService} to bind to for CTS test purposes. |
| * @param packageName The package name to set to. |
| */ |
| public void setTestCallDiagnosticService(@Nullable String packageName) { |
| if (TextUtils.isEmpty(packageName)) { |
| mTestPackageName = null; |
| } else { |
| mTestPackageName = packageName; |
| } |
| |
| Log.i(this, "setTestCallDiagnosticService: packageName=%s", packageName); |
| } |
| |
| /** |
| * Determines the active call diagnostic service, taking into account the test override. |
| * @return The package name of the active call diagnostic service. |
| */ |
| private @Nullable String getActiveCallDiagnosticService() { |
| if (mTestPackageName != null) { |
| return mTestPackageName; |
| } |
| |
| return mPackageName; |
| } |
| |
| /** |
| * If we are not already bound to the {@link CallDiagnosticService}, attempts to initiate a |
| * binding tho that service. |
| * @return {@code true} if we bound, {@code false} otherwise. |
| */ |
| private boolean maybeBindCallDiagnosticService() { |
| if (mConnection != null) { |
| return false; |
| } |
| |
| mConnection = new CallDiagnosticServiceConnection(); |
| boolean bound = bindCallDiagnosticService(mContextProxy.getCurrentUserHandle(), |
| getActiveCallDiagnosticService(), mConnection); |
| if (!bound) { |
| mConnection = null; |
| } |
| return bound; |
| } |
| |
| /** |
| * Performs binding to the {@link CallDiagnosticService}. |
| * @param userHandle user name to bind via. |
| * @param packageName package name of the CDS. |
| * @param serviceConnection The service connection to be notified of bind events. |
| * @return |
| */ |
| private boolean bindCallDiagnosticService(UserHandle userHandle, |
| String packageName, CallDiagnosticServiceConnection serviceConnection) { |
| |
| if (TextUtils.isEmpty(packageName)) { |
| Log.i(this, "bindCallDiagnosticService: no package; skip binding."); |
| return false; |
| } |
| |
| Intent intent = new Intent(CallDiagnosticService.SERVICE_INTERFACE) |
| .setPackage(packageName); |
| Log.i(this, "bindCallDiagnosticService: user %d.", userHandle.getIdentifier()); |
| List<ResolveInfo> entries = mContextProxy.queryIntentServicesAsUser(intent, 0, |
| userHandle.getIdentifier()); |
| if (entries.isEmpty()) { |
| Log.i(this, "bindCallDiagnosticService: %s has no service.", packageName); |
| return false; |
| } |
| |
| ResolveInfo entry = entries.get(0); |
| if (entry.serviceInfo == null) { |
| Log.i(this, "bindCallDiagnosticService: %s has no service info.", packageName); |
| return false; |
| } |
| |
| if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals( |
| Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE)) { |
| Log.i(this, "bindCallDiagnosticService: %s doesn't require " |
| + "BIND_CALL_DIAGNOSTIC_SERVICE.", packageName); |
| return false; |
| } |
| |
| ComponentName componentName = |
| new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name); |
| intent.setComponent(componentName); |
| if (mContextProxy.bindServiceAsUser( |
| intent, |
| serviceConnection, |
| Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, |
| UserHandle.CURRENT)) { |
| Log.d(this, "bindCallDiagnosticService, found service, waiting for it to connect"); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * If we are bound to a {@link CallDiagnosticService}, unbind from it. |
| */ |
| public void maybeUnbindCallScreeningService() { |
| if (mConnection != null) { |
| Log.i(this, "maybeUnbindCallScreeningService - unbinding from %s", |
| getActiveCallDiagnosticService()); |
| try { |
| mContextProxy.unbindService(mConnection); |
| mCallDiagnosticService = null; |
| mConnection = null; |
| } catch (IllegalArgumentException e) { |
| Log.i(this, "maybeUnbindCallScreeningService: Exception when unbind %s : %s", |
| getActiveCallDiagnosticService(), e.getMessage()); |
| } |
| } else { |
| Log.w(this, "maybeUnbindCallScreeningService - already unbound"); |
| } |
| } |
| |
| /** |
| * Implements the abstracted Telecom functionality the {@link CallDiagnosticServiceAdapter} |
| * depends on. |
| */ |
| private CallDiagnosticServiceAdapter.TelecomAdapter mTelecomAdapter = |
| new CallDiagnosticServiceAdapter.TelecomAdapter() { |
| |
| @Override |
| public void displayDiagnosticMessage(String callId, int messageId, CharSequence message) { |
| handleDisplayDiagnosticMessage(callId, messageId, message); |
| } |
| |
| @Override |
| public void clearDiagnosticMessage(String callId, int messageId) { |
| handleClearDiagnosticMessage(callId, messageId); |
| } |
| |
| @Override |
| public void sendDeviceToDeviceMessage(String callId, |
| @CallDiagnostics.MessageType int message, int value) { |
| handleSendD2DMessage(callId, message, value); |
| } |
| |
| @Override |
| public void overrideDisconnectMessage(String callId, CharSequence message) { |
| handleOverrideDisconnectMessage(callId, message); |
| } |
| }; |
| |
| /** |
| * Sends all calls to the specified {@link CallDiagnosticService}. |
| * @param callDiagnosticService the CDS to send calls to. |
| */ |
| private void handleConnectionComplete(@NonNull ICallDiagnosticService callDiagnosticService) { |
| mAdapter = new CallDiagnosticServiceAdapter(mTelecomAdapter, |
| getActiveCallDiagnosticService(), mLock); |
| try { |
| // Add adapter for communication back from the call diagnostic service to Telecom. |
| callDiagnosticService.setAdapter(mAdapter); |
| |
| // Loop through all the calls we've got ready to send since binding. |
| for (Call call : mCallIdMapper.getCalls()) { |
| sendCallToBoundService(call, callDiagnosticService); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "handleConnectionComplete: error=%s", e); |
| } |
| } |
| |
| /** |
| * Handles a request from a {@link CallDiagnosticService} to display a diagnostic message. |
| * @param callId the ID of the call to display the message for. |
| * @param message the message. |
| */ |
| private void handleDisplayDiagnosticMessage(@NonNull String callId, int messageId, |
| @Nullable CharSequence message) { |
| Call call = mCallIdMapper.getCall(callId); |
| if (call == null) { |
| Log.w(this, "handleDisplayDiagnosticMessage: callId=%s; msg=%d/%s; invalid call", |
| callId, messageId, message); |
| return; |
| } |
| Log.i(this, "handleDisplayDiagnosticMessage: callId=%s; msg=%d/%s", |
| callId, messageId, message); |
| if (mPlayerFactory != null) { |
| // Play that tone! |
| mPlayerFactory.createPlayer(InCallTonePlayer.TONE_IN_CALL_QUALITY_NOTIFICATION) |
| .startTone(); |
| } |
| call.displayDiagnosticMessage(messageId, message); |
| } |
| |
| /** |
| * Handles a request from a {@link CallDiagnosticService} to clear a previously displayed |
| * diagnostic message. |
| * @param callId the ID of the call to display the message for. |
| * @param messageId the message ID which was previous posted. |
| */ |
| private void handleClearDiagnosticMessage(@NonNull String callId, int messageId) { |
| Call call = mCallIdMapper.getCall(callId); |
| if (call == null) { |
| Log.w(this, "handleClearDiagnosticMessage: callId=%s; msg=%d; invalid call", |
| callId, messageId); |
| return; |
| } |
| Log.i(this, "handleClearDiagnosticMessage: callId=%s; msg=%d; invalid call", |
| callId, messageId); |
| call.clearDiagnosticMessage(messageId); |
| } |
| |
| /** |
| * Handles a request from a {@link CallDiagnosticService} to send a device to device message. |
| * @param callId The ID of the call to send the D2D message for. |
| * @param message The message type. |
| * @param value The message value. |
| */ |
| private void handleSendD2DMessage(@NonNull String callId, |
| @CallDiagnostics.MessageType int message, int value) { |
| Call call = mCallIdMapper.getCall(callId); |
| if (call == null) { |
| Log.w(this, "handleSendD2DMessage: callId=%s; msg=%d/%d; invalid call", callId, |
| message, value); |
| return; |
| } |
| Log.i(this, "handleSendD2DMessage: callId=%s; msg=%d/%d", callId, message, value); |
| call.sendDeviceToDeviceMessage(message, value); |
| } |
| |
| /** |
| * Handles a request from a {@link CallDiagnosticService} to override the disconnect message |
| * for a call. This is the response path from a previous call into the |
| * {@link CallDiagnosticService} via {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)}. |
| * @param callId The telecom call ID the disconnect override is pending for. |
| * @param message The new disconnect message, or {@code null} if no override. |
| */ |
| private void handleOverrideDisconnectMessage(@NonNull String callId, |
| @Nullable CharSequence message) { |
| Call call = mCallIdMapper.getCall(callId); |
| if (call == null) { |
| Log.w(this, "handleOverrideDisconnectMessage: callId=%s; msg=%s; invalid call", callId, |
| message); |
| return; |
| } |
| Log.i(this, "handleOverrideDisconnectMessage: callId=%s; msg=%s", callId, message); |
| call.handleOverrideDisconnectMessage(message); |
| } |
| |
| /** |
| * Sends a single call to the bound {@link CallDiagnosticService}. |
| * @param call The call to send. |
| * @param callDiagnosticService The CDS to send it to. |
| */ |
| private void sendCallToBoundService(@NonNull Call call, |
| @NonNull ICallDiagnosticService callDiagnosticService) { |
| try { |
| if (isConnected()) { |
| Log.w(this, "sendCallToBoundService: initializing %s", call.getId()); |
| callDiagnosticService.initializeDiagnosticCall(getParceledCall(call)); |
| } else { |
| Log.w(this, "sendCallToBoundService: not bound, skipping %s", call.getId()); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "sendCallToBoundService: callId=%s, exception=%s", call.getId(), e); |
| } |
| } |
| |
| /** |
| * Removes a call from a bound {@link CallDiagnosticService}. |
| * @param call The call to remove. |
| * @param callDiagnosticService The CDS to remove it from. |
| */ |
| private void removeCallFromBoundService(@NonNull Call call, |
| @NonNull ICallDiagnosticService callDiagnosticService) { |
| try { |
| if (isConnected()) { |
| callDiagnosticService.removeDiagnosticCall(call.getId()); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "removeCallFromBoundService: callId=%s, exception=%s", call.getId(), e); |
| } |
| } |
| |
| /** |
| * @return {@code true} if the call diagnostic service is bound/connected. |
| */ |
| public boolean isConnected() { |
| return mCallDiagnosticService != null; |
| } |
| |
| /** |
| * Updates the Call diagnostic service with changes to a call. |
| * @param call The updated call. |
| */ |
| private void updateCall(@NonNull Call call) { |
| try { |
| if (isConnected()) { |
| mCallDiagnosticService.updateCall(getParceledCall(call)); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "updateCall: callId=%s, exception=%s", call.getId(), e); |
| } |
| } |
| |
| /** |
| * Updates the call diagnostic service with a received bluetooth quality report. |
| * @param call The call. |
| * @param report The bluetooth call quality report. |
| */ |
| private void handleBluetoothCallQualityReport(@NonNull Call call, |
| @NonNull BluetoothCallQualityReport report) { |
| try { |
| if (isConnected()) { |
| mCallDiagnosticService.receiveBluetoothCallQualityReport(report); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "handleBluetoothCallQualityReport: callId=%s, exception=%s", call.getId(), |
| e); |
| } |
| } |
| |
| /** |
| * Informs a CallDiagnosticService of an incoming device to device message which was received |
| * via the carrier network. |
| * @param call the call the message was received via. |
| * @param messageType The message type. |
| * @param messageValue The message value. |
| */ |
| private void handleReceivedDeviceToDeviceMessage(@NonNull Call call, int messageType, |
| int messageValue) { |
| try { |
| if (isConnected()) { |
| mCallDiagnosticService.receiveDeviceToDeviceMessage(call.getId(), messageType, |
| messageValue); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "handleReceivedDeviceToDeviceMessage: callId=%s, exception=%s", |
| call.getId(), e); |
| } |
| } |
| |
| /** |
| * Handles a reported {@link CallQuality} report from a {@link android.telecom.Connection}. |
| * @param call The call the report originated from. |
| * @param callQualityReport The {@link CallQuality} report. |
| */ |
| private void handleCallQualityReport(@NonNull Call call, |
| @NonNull CallQuality callQualityReport) { |
| try { |
| if (isConnected()) { |
| mCallDiagnosticService.callQualityChanged(call.getId(), callQualityReport); |
| } |
| } catch (RemoteException e) { |
| Log.w(this, "handleCallQualityReport: callId=%s, exception=%s", |
| call.getId(), e); |
| } |
| } |
| |
| /** |
| * Get a parcelled representation of a call for transport to the service. |
| * @param call The call. |
| * @return The parcelled call. |
| */ |
| private @NonNull ParcelableCall getParceledCall(@NonNull Call call) { |
| return ParcelableCallUtils.toParcelableCall( |
| call, |
| false /* includeVideoProvider */, |
| null /* phoneAcctRegistrar */, |
| false /* supportsExternalCalls */, |
| false /* includeRttCall */, |
| false /* isForSystemDialer */ |
| ); |
| } |
| |
| /** |
| * Dumps the state of the {@link CallDiagnosticServiceController}. |
| * |
| * @param pw The {@code IndentingPrintWriter} to write the state to. |
| */ |
| public void dump(IndentingPrintWriter pw) { |
| pw.print("activeCallDiagnosticService: "); |
| pw.println(getActiveCallDiagnosticService()); |
| pw.print("isConnected: "); |
| pw.println(isConnected()); |
| } |
| } |