Merge "Stop mocking IRoleManager."
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index c0f2816..8d960d9 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -192,7 +193,10 @@
 import com.android.services.telephony.TelephonyConnectionService;
 import com.android.telephony.Rlog;
 
+import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -202,7 +206,10 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
@@ -3924,6 +3931,50 @@
     }
 
     /**
+     * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
+    @Override
+    public boolean isCrossSimCallingEnabledByUser(int subId) {
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isCrossSimCallingEnabledByUser");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            return ImsManager.getInstance(mApp,
+                    getSlotIndexOrException(subId)).isCrossSimCallingEnabledByUser();
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
+     * Requires MODIFY_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
+     *                 false otherwise
+     */
+    @Override
+    public void setCrossSimCallingEnabled(int subId, boolean isEnabled) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setCrossSimCallingEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+                    .setCrossSimCallingEnabled(isEnabled);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
      * @param subId The subscription to use to check the configuration.
      */
@@ -6918,6 +6969,84 @@
     }
 
     @Override
+    public void uploadCallComposerPicture(int subscriptionId, String callingPackage,
+            ParcelFileDescriptor fd, ResultReceiver callback) {
+        try {
+            if (!Objects.equals(mApp.getPackageManager().getPackageUid(callingPackage, 0),
+                    Binder.getCallingUid())) {
+                throw new SecurityException("Package uid and package name do not match: "
+                        + "uid=" + Binder.getCallingUid() + ", packageName=" + callingPackage);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new SecurityException("Package name invalid:" + callingPackage);
+        }
+        RoleManager rm = mApp.getSystemService(RoleManager.class);
+        List<String> dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER);
+        if (!dialerRoleHolders.contains(callingPackage)) {
+            throw new SecurityException("App must be the dialer role holder to"
+                    + " upload a call composer pic");
+        }
+
+        Executors.newSingleThreadExecutor().execute(() -> {
+            ByteArrayOutputStream output = new ByteArrayOutputStream(
+                    (int) TelephonyManager.getMaximumCallComposerPictureSize());
+            InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd);
+            boolean readUntilEnd = false;
+            int totalBytesRead = 0;
+            byte[] buffer = new byte[16 * 1024];
+            while (true) {
+                int numRead;
+                try {
+                    numRead = input.read(buffer);
+                } catch (IOException e) {
+                    try {
+                        fd.checkError();
+                        callback.send(TelephonyManager.CallComposerException.ERROR_INPUT_CLOSED,
+                                null);
+                    } catch (IOException e1) {
+                        // This means that the other side closed explicitly with an error. If this
+                        // happens, log and ignore.
+                        loge("Remote end of call composer picture pipe closed: " + e1);
+                    }
+                    break;
+                }
+                if (numRead == -1) {
+                    readUntilEnd = true;
+                    break;
+                }
+                totalBytesRead += numRead;
+                if (totalBytesRead > TelephonyManager.getMaximumCallComposerPictureSize()) {
+                    loge("Too many bytes read for call composer picture: " + totalBytesRead);
+                    try {
+                        input.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                    break;
+                }
+                output.write(buffer, 0, numRead);
+            }
+            // Generally, the remote end will close the file descriptors. The only case where we
+            // close is above, where the picture size is too big.
+
+            try {
+                fd.checkError();
+            } catch (IOException e) {
+                loge("Remote end for call composer closed with an error: " + e);
+                return;
+            }
+
+            // TODO: pass along the bytes read to the carrier somehow
+
+            ParcelUuid result = new ParcelUuid(UUID.randomUUID());
+            // TODO: cache this uuid that's been associated with the picture
+            Bundle outputResult = new Bundle();
+            outputResult.putParcelable(TelephonyManager.KEY_CALL_COMPOSER_PICTURE_HANDLE, result);
+            callback.send(-1, outputResult);
+        });
+    }
+
+    @Override
     public void enableVideoCalling(boolean enable) {
         final Phone defaultPhone = getDefaultPhone();
         enforceModifyPermission();
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index fcf7f1b..f45b084 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -991,6 +991,9 @@
             return mMmTelManager.isAvailable(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)
                     || mMmTelManager.isAvailable(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)
+                    || mMmTelManager.isAvailable(
+                            ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         }
     }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 278e387..4a997a2 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -1302,6 +1302,8 @@
                 isNetworkIdentifiedEmergencyCall());
         newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
                 isAdhocConferenceCall());
+        newProperties = changeBitmask(newProperties, PROPERTY_CROSS_SIM,
+                isCrossSimCall());
 
         if (getConnectionProperties() != newProperties) {
             setTelephonyConnectionProperties(newProperties);
@@ -2378,6 +2380,16 @@
     }
 
     /**
+     * Determines if the current connection is cross sim calling
+     */
+    private boolean isCrossSimCall() {
+        return mOriginalConnection != null
+                && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
+                && mOriginalConnection instanceof ImsPhoneConnection
+                && ((ImsPhoneConnection) mOriginalConnection).isCrossSimCall();
+    }
+
+    /**
      * Determines if the current connection is pullable.
      *
      * A connection is deemed to be pullable if the original connection capabilities state that it
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
index 0ff6cc1..477bbc0 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
@@ -188,6 +188,9 @@
         sb.append("}, \nIWLAN: ");
         sb.append("{");
         sb.append(caps.get(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN));
+        sb.append("}, \nCROSS-SIM: ");
+        sb.append("{");
+        sb.append(caps.get(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM));
         sb.append("}");
         mCapEnabledText.setText(sb.toString());
     }