Wiring up RTP Transport.

- Add support for loading config.xml values pertinent to
ImsPhoneCallTracker on phone initialiation.
- Add support for new shell command "d2d send -t type -v value"; this
lets us test sending D2D comms messages.
- More logging!

Test: Performed test of new adb shell command and verified that logs
indicate proper passthrough of messages.
Test: Added unit tests to verify that the D2D communicator will be set up
in a TelephonyConnection when it's an IMS connection and D2D comms is
enabled.
Bug: 163085177

Change-Id: I042e7da030e6dd41d2c20f095416185a14194213
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index ce8e9b3..5c97597 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -69,6 +69,8 @@
 import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
 import com.android.internal.telephony.ims.ImsResolver;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
@@ -360,6 +362,24 @@
                         defaultImsRcsPackage, PhoneFactory.getPhones().length,
                         new ImsFeatureBinderRepository());
                 mImsResolver.initialize();
+
+                // With the IMS phone created, load static config.xml values from the phone process
+                // so that it can be provided to the ImsPhoneCallTracker.
+                for (Phone p : PhoneFactory.getPhones()) {
+                    Phone imsPhone = p.getImsPhone();
+                    if (imsPhone != null && imsPhone instanceof ImsPhone) {
+                        ImsPhone theImsPhone = (ImsPhone) imsPhone;
+                        if (theImsPhone.getCallTracker() instanceof ImsPhoneCallTracker) {
+                            ImsPhoneCallTracker ict = (ImsPhoneCallTracker)
+                                    theImsPhone.getCallTracker();
+
+                            ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
+                            config.isD2DCommunicationSupported = getResources().getBoolean(
+                                    R.bool.config_use_device_to_device_communication);
+                            ict.setConfig(config);
+                        }
+                    }
+                }
                 RcsProvisioningMonitor.make(this);
             }
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 3e608d2..73fbb4b 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -9759,6 +9759,32 @@
     }
 
     /**
+     * Sends a device to device communication message.  Only usable via shell.
+     * @param message message to send.
+     * @param value message value.
+     */
+    @Override
+    public void sendDeviceToDeviceMessage(int message, int value) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setCarrierSingleRegistrationEnabledOverride");
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            TelephonyConnectionService service =
+                    TelecomAccountRegistry.getInstance(null).getTelephonyConnectionService();
+            if (service == null) {
+                Rlog.e(LOG_TAG, "sendDeviceToDeviceMessage: not in a call.");
+                return;
+            }
+            service.sendTestDeviceToDeviceMessage(message, value);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+
+    /**
      * Gets the config of RCS VoLTE single registration enabled for the device.
      */
     @Override
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index ed719b6..f5bfe10 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,6 +16,11 @@
 
 package com.android.phone;
 
+import static com.android.internal.telephony.d2d.Communicator.MESSAGE_CALL_AUDIO_CODEC;
+import static com.android.internal.telephony.d2d.Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE;
+import static com.android.internal.telephony.d2d.Communicator.MESSAGE_DEVICE_BATTERY_STATE;
+import static com.android.internal.telephony.d2d.Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE;
+
 import android.content.Context;
 import android.os.Binder;
 import android.os.PersistableBundle;
@@ -31,6 +36,7 @@
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.d2d.Communicator;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
@@ -93,6 +99,9 @@
     private static final String SRC_SET_CARRIER_ENABLED = "set-carrier-enabled";
     private static final String SRC_GET_CARRIER_ENABLED = "get-carrier-enabled";
 
+    private static final String D2D_SUBCOMMAND = "d2d";
+    private static final String D2D_SEND = "send";
+
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
 
@@ -175,6 +184,8 @@
                 return handleEndBlockSuppressionCommand();
             case GBA_SUBCOMMAND:
                 return handleGbaCommand();
+            case D2D_SUBCOMMAND:
+                return handleD2dCommand();
             case SINGLE_REGISTATION_CONFIG:
                 return handleSingleRegistrationConfigCommand();
             default: {
@@ -210,6 +221,23 @@
         onHelpCc();
         onHelpGba();
         onHelpSrc();
+        onHelpD2D();
+    }
+
+    private void onHelpD2D() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("D2D Comms Commands:");
+        pw.println("  d2d send TYPE VALUE");
+        pw.println("    Sends a D2D message of specified type and value.");
+        pw.println("    Type: " + MESSAGE_CALL_RADIO_ACCESS_TYPE + " - "
+                + Communicator.messageToString(MESSAGE_CALL_RADIO_ACCESS_TYPE));
+        pw.println("    Type: " + MESSAGE_CALL_AUDIO_CODEC + " - " + Communicator.messageToString(
+                MESSAGE_CALL_AUDIO_CODEC));
+        pw.println("    Type: " + MESSAGE_DEVICE_BATTERY_STATE + " - "
+                        + Communicator.messageToString(
+                        MESSAGE_DEVICE_BATTERY_STATE));
+        pw.println("    Type: " + MESSAGE_DEVICE_NETWORK_COVERAGE + " - "
+                + Communicator.messageToString(MESSAGE_DEVICE_NETWORK_COVERAGE));
     }
 
     private void onHelpIms() {
@@ -540,6 +568,64 @@
         return -1;
     }
 
+    private int handleD2dCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpD2D();
+            return 0;
+        }
+
+        switch (arg) {
+            case D2D_SEND: {
+                return handleD2dSendCommand();
+            }
+        }
+
+        return -1;
+    }
+
+    private int handleD2dSendCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        String opt;
+        int messageType = -1;
+        int messageValue = -1;
+
+
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpD2D();
+            return 0;
+        }
+        try {
+            messageType = Integer.parseInt(arg);
+        } catch (NumberFormatException e) {
+            errPw.println("message type must be a valid integer");
+            return -1;
+        }
+
+        arg = getNextArg();
+        if (arg == null) {
+            onHelpD2D();
+            return 0;
+        }
+        try {
+            messageValue = Integer.parseInt(arg);
+        } catch (NumberFormatException e) {
+            errPw.println("message value must be a valid integer");
+            return -1;
+        }
+        
+        try {
+            mInterface.sendDeviceToDeviceMessage(messageType, messageValue);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "d2d send error: " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+
+        return 0;
+    }
+
     // ims set-ims-service
     private int handleImsSetServiceCommand() {
         PrintWriter errPw = getErrPrintWriter();
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 6814a00..45c00e4 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -3168,10 +3168,14 @@
      */
     private void maybeConfigureDeviceToDeviceCommunication() {
         if (!getPhone().getContext().getResources().getBoolean(
-                R.bool.config_use_device_to_device_communication) || !isImsConnection()) {
+                R.bool.config_use_device_to_device_communication)) {
             Log.d(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D.");
             return;
         }
+        if (!isImsConnection()) {
+            Log.d(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection.");
+            return;
+        }
         // Implement abstracted out RTP functionality the RTP transport depends on.
         RtpAdapter rtpAdapter = new RtpAdapter() {
             @Override
@@ -3188,8 +3192,10 @@
             public void sendRtpHeaderExtensions(
                     @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
                 if (!isImsConnection()) {
-                    Log.w(this, "sendRtpHeaderExtensions: not an ims connection.");
+                    Log.w(TelephonyConnection.this, "sendRtpHeaderExtensions: not an ims conn.");
                 }
+                Log.d(TelephonyConnection.this, "sendRtpHeaderExtensions: sending %d messages",
+                        rtpHeaderExtensions.size());
                 ImsPhoneConnection originalConnection =
                         (ImsPhoneConnection) mOriginalConnection;
                 originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions);
@@ -3202,6 +3208,13 @@
     }
 
     /**
+     * @return The D2D communication class, or {@code null} if not set up.
+     */
+    public @Nullable Communicator getCommunicator() {
+        return mCommunicator;
+    }
+
+    /**
      * Called by {@link Communicator} associated with this {@link TelephonyConnection} when there
      * are incoming device-to-device messages received.
      * @param messages the incoming messages.
@@ -3210,6 +3223,14 @@
     public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) {
         Log.i(this, "onMessagesReceived: got d2d messages: %s", messages);
         // TODO: Actually do something WITH the messages.
+
+        // TODO: Remove this prior to launch.
+        // This is just here for debug purposes; send as a connection event so that it
+        // will be output in the Telecom logs.
+        for (Communicator.Message msg : messages) {
+            sendConnectionEvent("D2D_" + Communicator.messageToString(msg.getType())
+                + "_" + Communicator.valueToString(msg.getType(), msg.getValue()), null);
+        }
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 56012c8..4b641cd 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -63,6 +63,7 @@
 import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.d2d.Communicator;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
@@ -77,6 +78,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -2484,6 +2486,31 @@
         conference.addTelephonyConferenceListener(mTelephonyConferenceListener);
     }
 
+    /**
+     * Sends a test device to device message on the active call which supports it.
+     * Used exclusively from the telephony shell command to send a test message.
+     *
+     * @param message the message
+     * @param value the value
+     */
+    public void sendTestDeviceToDeviceMessage(int message, int value) {
+       getAllConnections().stream()
+               .filter(f -> f instanceof TelephonyConnection)
+               .forEach(t -> {
+                        TelephonyConnection tc = (TelephonyConnection) t;
+                        Communicator c = tc.getCommunicator();
+                        if (c == null) {
+                            Log.w(this, "sendTestDeviceToDeviceMessage: D2D not enabled");
+                            return;
+                        }
+
+                        c.sendMessages(new HashSet<Communicator.Message>() {{
+                            add(new Communicator.Message(message, value));
+                        }});
+
+       });
+    }
+
     private PhoneAccountHandle adjustAccountHandle(Phone phone,
             PhoneAccountHandle origAccountHandle) {
         int origSubId = PhoneUtils.getSubIdForPhoneAccountHandle(origAccountHandle);