Merge "Resolve Telecom CountryDetector dependencies" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c6f5e9c..90e4bd9 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -64,7 +64,7 @@
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
     <uses-permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"/>
     <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
-    <uses-permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"/>
+    <uses-permission android:name="android.permission.ACCESS_LAST_KNOWN_CELL_ID"/>
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
 
     <permission android:name="android.permission.BROADCAST_CALLLOG_INFO"
diff --git a/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java b/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java
index af79da3..09e927c 100644
--- a/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java
+++ b/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java
@@ -156,25 +156,26 @@
         List<Integer> dataCollectionTypes = getDataCollectionTypes(reason);
         boolean invokeTelephonyPersistApi = false;
         CallEventTimestamps ts = mEmergencyCallsMap.get(call);
-        EmergencyCallDiagnosticParams dp =
-                new EmergencyCallDiagnosticParams();
+        EmergencyCallDiagnosticParams.Builder callDiagnosticBuilder =
+                new EmergencyCallDiagnosticParams.Builder();
         for (Integer dataCollectionType : dataCollectionTypes) {
             switch (dataCollectionType) {
                 case COLLECTION_TYPE_TELECOM_STATE:
                     if (isTelecomDumpCollectionEnabled()) {
-                        dp.setTelecomDumpSysCollection(true);
+                        callDiagnosticBuilder.setTelecomDumpSysCollectionEnabled(true);
                         invokeTelephonyPersistApi = true;
                     }
                     break;
                 case COLLECTION_TYPE_TELEPHONY_STATE:
                     if (isTelephonyDumpCollectionEnabled()) {
-                        dp.setTelephonyDumpSysCollection(true);
+                        callDiagnosticBuilder.setTelephonyDumpSysCollectionEnabled(true);
                         invokeTelephonyPersistApi = true;
                     }
                     break;
                 case COLLECTION_TYPE_LOGCAT_BUFFERS:
                     if (isLogcatCollectionEnabled()) {
-                        dp.setLogcatCollection(true, ts.getCallCreatedTime());
+                        callDiagnosticBuilder.setLogcatCollectionStartTimeMillis(
+                                ts.getCallCreatedTime());
                         invokeTelephonyPersistApi = true;
                     }
                     break;
@@ -191,6 +192,7 @@
                 default:
             }
         }
+        EmergencyCallDiagnosticParams dp = callDiagnosticBuilder.build();
         if (invokeTelephonyPersistApi) {
             mAsyncTaskExecutor.execute(new Runnable() {
                 @Override
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index ad3d26a..4ad5f3b 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1932,7 +1932,8 @@
 
         // Actually try binding to the UI InCallService.
         if (inCallServiceConnection.connect(call) ==
-                InCallServiceConnection.CONNECTION_SUCCEEDED || call.isSelfManaged()) {
+                InCallServiceConnection.CONNECTION_SUCCEEDED || (call != null
+                && call.isSelfManaged())) {
             // Only connect to the non-ui InCallServices if we actually connected to the main UI
             // one, or if the call is self-managed (in which case we'd still want to keep Wear, BT,
             // etc. informed.
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 1701e5a..61fa9ba 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -77,6 +77,7 @@
 import com.android.internal.telecom.ICallControl;
 import com.android.internal.telecom.ICallEventCallback;
 import com.android.internal.telecom.ITelecomService;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.flags.FeatureFlags;
@@ -807,6 +808,13 @@
                         // Validate the profile boundary of the given image URI.
                         validateAccountIconUserBoundary(account.getIcon());
 
+                        if (mTelephonyFeatureFlags.simultaneousCallingIndications()
+                                && account.hasSimultaneousCallingRestriction()) {
+                            validateSimultaneousCallingPackageNames(
+                                    account.getAccountHandle().getComponentName().getPackageName(),
+                                    account.getSimultaneousCallingRestriction());
+                        }
+
                         final long token = Binder.clearCallingIdentity();
                         try {
                             Log.i(this, "registerPhoneAccount: account=%s",
@@ -3292,4 +3300,20 @@
             }
         }
     }
+
+    private void validateSimultaneousCallingPackageNames(String appPackageName,
+            Set<PhoneAccountHandle> handles) {
+        for (PhoneAccountHandle handle : handles) {
+            ComponentName name = handle.getComponentName();
+            if (name == null) {
+                throw new IllegalArgumentException("ComponentName is null");
+            }
+            String restrictionPackageName = name.getPackageName();
+            if (!appPackageName.equals(restrictionPackageName)) {
+                throw new SecurityException("The package name of the PhoneAccount does not "
+                        + "match one or more of the package names set in the simultaneous "
+                        + "calling restriction.");
+            }
+        }
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/CallControlTest.java b/tests/src/com/android/server/telecom/tests/CallControlTest.java
index 2613206..c69521a 100644
--- a/tests/src/com/android/server/telecom/tests/CallControlTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallControlTest.java
@@ -39,14 +39,7 @@
 import java.util.UUID;
 
 public class CallControlTest extends TelecomTestCase {
-
-    private static final PhoneAccountHandle mHandle = new PhoneAccountHandle(
-            new ComponentName("foo", "bar"), "1");
-
-    @Mock
-    private ICallControl mICallControl;
-    @Mock
-    private ClientTransactionalServiceRepository mRepository;
+    @Mock private ICallControl mICallControl;
     private static final String CALL_ID_1 = UUID.randomUUID().toString();
 
     @Override
@@ -64,15 +57,7 @@
 
     @Test
     public void testGetCallId() {
-        CallControl control = new CallControl(CALL_ID_1, mICallControl, mRepository, mHandle);
+        CallControl control = new CallControl(CALL_ID_1, mICallControl);
         assertEquals(CALL_ID_1, control.getCallId().toString());
     }
-
-    @Test
-    public void testCallControlHitsIllegalStateException() {
-        CallControl control = new CallControl(CALL_ID_1, null, mRepository, mHandle);
-        assertThrows(IllegalStateException.class, () ->
-                control.setInactive(Runnable::run, result -> {
-                }));
-    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index bb6dce0..24b23af 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -111,7 +111,10 @@
 
 import java.lang.reflect.Method;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.IntConsumer;
 
@@ -846,6 +849,62 @@
 
     @SmallTest
     @Test
+    public void testRegisterPhoneAccountSimultaneousCallingVerification() throws RemoteException {
+        doReturn(true).when(mTelephonyFeatureFlags).simultaneousCallingIndications();
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
+        String packageNameToUse = "com.android.officialpackage";
+        PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
+                packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
+        PhoneAccountHandle phAllowedRestriction = new PhoneAccountHandle(new ComponentName(
+                packageNameToUse, "cs"), "test2", Binder.getCallingUserHandle());
+
+        PhoneAccount phoneAccountEmptyRestriction = makePhoneAccount(phHandle)
+                .setSimultaneousCallingRestriction(Collections.emptySet())
+                .build();
+        try {
+            mTSIBinder.registerPhoneAccount(phoneAccountEmptyRestriction, CALLING_PACKAGE);
+            verify(mFakePhoneAccountRegistrar).registerPhoneAccount(phoneAccountEmptyRestriction);
+        } catch (SecurityException e) {
+            fail("registerPhoneAccount must not throw a SecurityException if there is a "
+                    + " restriction registered with the same package name.");
+        }
+
+        Set<PhoneAccountHandle> restriction = new HashSet<>(3);
+        restriction.add(phAllowedRestriction);
+        PhoneAccount phoneAccount = makePhoneAccount(phHandle)
+                .setSimultaneousCallingRestriction(restriction)
+                .build();
+
+        try {
+            mTSIBinder.registerPhoneAccount(phoneAccount, CALLING_PACKAGE);
+            verify(mFakePhoneAccountRegistrar).registerPhoneAccount(phoneAccount);
+        } catch (SecurityException e) {
+            fail("registerPhoneAccount must not throw a SecurityException if there is a "
+                    + " restriction registered with the same package name.");
+        }
+
+        String anotherPackageName = "com.android.anotherpackage";
+        PhoneAccountHandle phDisallowedRestriction = new PhoneAccountHandle(new ComponentName(
+                anotherPackageName, "cs"), "test", Binder.getCallingUserHandle());
+        restriction.add(phDisallowedRestriction);
+        phoneAccount = makePhoneAccount(phHandle)
+                .setSimultaneousCallingRestriction(restriction)
+                .build();
+
+        try {
+            mTSIBinder.registerPhoneAccount(phoneAccount, CALLING_PACKAGE);
+            // there should not be another call to registerPhoneAccount
+            verify(mFakePhoneAccountRegistrar, times(1)).registerPhoneAccount(phoneAccount);
+            fail("registerPhoneAccount must throw a SecurityException if there is a "
+                    + " restriction registered with a different package name.");
+        } catch (SecurityException e) {
+            //expected
+        }
+    }
+
+    @SmallTest
+    @Test
     public void testRegisterPhoneAccountWithoutPermissionAnomalyReported() throws RemoteException {
         PhoneAccountHandle handle = new PhoneAccountHandle(
                 new ComponentName("package", "cs"), "test", Binder.getCallingUserHandle());