Merge "Resolve referencing for preferred audio routing" into main
diff --git a/flags/telecom_connection_service_wrapper_flags.aconfig b/flags/telecom_connection_service_wrapper_flags.aconfig
index 38e5e13..8e77af5 100644
--- a/flags/telecom_connection_service_wrapper_flags.aconfig
+++ b/flags/telecom_connection_service_wrapper_flags.aconfig
@@ -7,4 +7,15 @@
   namespace: "telecom"
   description: "Ensure that the associatedCallCount of CS and RCS is accurately being tracked."
   bug: "286154316"
+}
+
+# OWNER=tjstuart TARGET=24Q4
+flag {
+  name: "csw_service_interface_is_null"
+  namespace: "telecom"
+  description: "fix potential NPE in onCreateConnection when the ServiceInterface is cleared out"
+  bug: "364811868"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+      }
 }
\ No newline at end of file
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 0a99903..4283b7b 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -2110,8 +2110,9 @@
     private int getCurrentCallSupportedRoutes() {
         int supportedRoutes = CallAudioState.ROUTE_ALL;
 
-        if (mCallsManager.getForegroundCall() != null) {
-            supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes();
+        Call foregroundCall = mCallsManager.getForegroundCall();
+        if (foregroundCall != null) {
+            supportedRoutes &= foregroundCall.getSupportedAudioRoutes();
         }
 
         return supportedRoutes;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index a7ad3b8..1e6d2bc 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -2066,7 +2066,8 @@
                         return CompletableFuture.completedFuture(
                                 Collections.singletonList(suggestion));
                     }
-                    return PhoneAccountSuggestionHelper.bindAndGetSuggestions(mContext,
+                    Context userContext = mContext.createContextAsUser(getCurrentUserHandle(), 0);
+                    return PhoneAccountSuggestionHelper.bindAndGetSuggestions(userContext,
                             finalCall.getHandle(), potentialPhoneAccounts);
                 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS", mLock));
 
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 5e00a72..44686b7 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1712,14 +1712,19 @@
                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
                         .build();
                 try {
-                    mServiceInterface.createConnection(
-                            call.getConnectionManagerPhoneAccount(),
-                            callId,
-                            connectionRequest,
-                            call.shouldAttachToExistingConnection(),
-                            call.isUnknown(),
-                            Log.getExternalSession(TELECOM_ABBREVIATION));
-
+                    if (mFlags.cswServiceInterfaceIsNull() && mServiceInterface == null) {
+                        mPendingResponses.remove(callId).handleCreateConnectionFailure(
+                                new DisconnectCause(DisconnectCause.ERROR,
+                                        "CSW#oCC ServiceInterface is null"));
+                    } else {
+                        mServiceInterface.createConnection(
+                                call.getConnectionManagerPhoneAccount(),
+                                callId,
+                                connectionRequest,
+                                call.shouldAttachToExistingConnection(),
+                                call.isUnknown(),
+                                Log.getExternalSession(TELECOM_ABBREVIATION));
+                    }
                 } catch (RemoteException e) {
                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
diff --git a/src/com/android/server/telecom/PhoneAccountSuggestionHelper.java b/src/com/android/server/telecom/PhoneAccountSuggestionHelper.java
index 438ee68..ab55703 100644
--- a/src/com/android/server/telecom/PhoneAccountSuggestionHelper.java
+++ b/src/com/android/server/telecom/PhoneAccountSuggestionHelper.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telecom.Log;
 import android.telecom.Logging.Session;
 import android.telecom.PhoneAccountHandle;
@@ -46,6 +47,7 @@
 public class PhoneAccountSuggestionHelper {
     private static final String TAG = PhoneAccountSuggestionHelper.class.getSimpleName();
     private static ComponentName sOverrideComponent;
+    private static UserHandle sOverrideUserHandle;
 
     /**
      * @return A future (possible already complete) that contains a list of suggestions.
@@ -53,6 +55,15 @@
     public static CompletableFuture<List<PhoneAccountSuggestion>>
     bindAndGetSuggestions(Context context, Uri handle,
             List<PhoneAccountHandle> availablePhoneAccounts) {
+        Context userContext;
+        if (sOverrideUserHandle != null) {
+            userContext = context.createContextAsUser(sOverrideUserHandle, 0);
+            Log.i(TAG, "bindAndGetSuggestions created context as user;  userContext=%s",
+                    userContext);
+        } else {
+            userContext = context;
+        }
+
         // Use the default list if there's no handle
         if (handle == null) {
             return CompletableFuture.completedFuture(getDefaultSuggestions(availablePhoneAccounts));
@@ -60,7 +71,7 @@
         String number = PhoneNumberUtils.extractNetworkPortion(handle.getSchemeSpecificPart());
 
         // Use the default list if there's no service on the device.
-        ServiceInfo suggestionServiceInfo = getSuggestionServiceInfo(context);
+        ServiceInfo suggestionServiceInfo = getSuggestionServiceInfo(userContext);
         if (suggestionServiceInfo == null) {
             return CompletableFuture.completedFuture(getDefaultSuggestions(availablePhoneAccounts));
         }
@@ -124,7 +135,7 @@
             }
         };
 
-        if (!context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE)) {
+        if (!userContext.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE)) {
             Log.i(TAG, "Cancelling suggestion process due to bind failure.");
             future.complete(getDefaultSuggestions(availablePhoneAccounts));
         }
@@ -143,7 +154,7 @@
                         Log.endSession();
                     }
                 },
-                Timeouts.getPhoneAccountSuggestionServiceTimeout(context.getContentResolver()));
+                Timeouts.getPhoneAccountSuggestionServiceTimeout(userContext.getContentResolver()));
         return future;
     }
 
@@ -162,10 +173,25 @@
     }
 
     private static ServiceInfo getSuggestionServiceInfo(Context context) {
-        PackageManager packageManager = context.getPackageManager();
+        Context userContext;
+        if (sOverrideUserHandle != null) {
+            userContext = context.createContextAsUser(sOverrideUserHandle, 0);
+            Log.i(TAG, "getSuggestionServiceInfo: Created context as user; userContext= %s",
+                    userContext);
+        } else {
+            userContext = context;
+        }
+
+        PackageManager packageManager = userContext.getPackageManager();
+
         Intent queryIntent = new Intent();
         queryIntent.setAction(PhoneAccountSuggestionService.SERVICE_INTERFACE);
 
+        if (packageManager == null) {
+            Log.i(TAG, "getSuggestionServiceInfo: PackageManager is null. Using defaults.");
+            return null;
+        }
+
         List<ResolveInfo> services;
         if (sOverrideComponent == null) {
             services = packageManager.queryIntentServices(queryIntent,
@@ -199,6 +225,15 @@
         }
     }
 
+    static void setOverrideUserHandle(UserHandle userHandle) {
+        try {
+            sOverrideUserHandle = userHandle;
+        } catch (Exception e) {
+            sOverrideUserHandle = null;
+            throw e;
+        }
+    }
+
     private static List<PhoneAccountSuggestion> getDefaultSuggestions(
             List<PhoneAccountHandle> phoneAccountHandles) {
         return phoneAccountHandles.stream().map(phoneAccountHandle ->
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 20320f2..b8141bf 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -2555,7 +2555,8 @@
         }
 
         @Override
-        public void setTestPhoneAcctSuggestionComponent(String flattenedComponentName) {
+        public void setTestPhoneAcctSuggestionComponent(String flattenedComponentName,
+                UserHandle userHandle) {
             try {
                 Log.startSession("TSI.sPASA");
                 enforceModifyPermission();
@@ -2565,6 +2566,7 @@
                 }
                 synchronized (mLock) {
                     PhoneAccountSuggestionHelper.setOverrideServiceName(flattenedComponentName);
+                    PhoneAccountSuggestionHelper.setOverrideUserHandle(userHandle);
                 }
             } finally {
                 Log.endSession();
diff --git a/src/com/android/server/telecom/TelecomShellCommand.java b/src/com/android/server/telecom/TelecomShellCommand.java
index 557002c..11ceb26 100644
--- a/src/com/android/server/telecom/TelecomShellCommand.java
+++ b/src/com/android/server/telecom/TelecomShellCommand.java
@@ -341,7 +341,8 @@
 
     private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
         final String componentName = getNextArg();
-        mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
+        final UserHandle userHandle = getUserHandleFromArgs();
+        mTelecomService.setTestPhoneAcctSuggestionComponent(componentName, userHandle);
     }
 
     private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
@@ -457,6 +458,22 @@
         mTelecomService.requestLogMark(message);
     }
 
+    private UserHandle getUserHandleFromArgs() throws RemoteException {
+        if (TextUtils.isEmpty(peekNextArg())) {
+            return null;
+        }
+        final String userSnInStr = getNextArgRequired();
+        UserHandle userHandle;
+        try {
+            final int userSn = Integer.parseInt(userSnInStr);
+            userHandle = UserHandle.of(getUserManager().getUserHandle(userSn));
+        } catch (NumberFormatException ex) {
+            Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
+            throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
+        }
+        return userHandle;
+    }
+
     private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
         if (TextUtils.isEmpty(peekNextArg())) {
             return null;