maintain phone accounts only for enabled accounts

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java | 91 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java            | 13 
2 files changed, 77 insertions(+), 27 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java 🔗

@@ -22,6 +22,7 @@ import android.util.Log;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -44,11 +45,16 @@ import java.util.Collections;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 public class CallIntegrationConnectionService extends ConnectionService {
 
+    private static final ExecutorService ACCOUNT_REGISTRATION_EXECUTOR =
+            Executors.newSingleThreadExecutor();
+
     private ListenableFuture<ServiceConnectionService> serviceFuture;
 
     @Override
@@ -210,16 +216,36 @@ public class CallIntegrationConnectionService extends ConnectionService {
         return jingleRtpConnection.getCallIntegration();
     }
 
-    public static void registerPhoneAccount(final Context context, final Account account) {
+    public static void togglePhoneAccountAsync(final Context context, final Account account) {
+        ACCOUNT_REGISTRATION_EXECUTOR.execute(() -> togglePhoneAccount(context, account));
+    }
+
+    private static void togglePhoneAccount(final Context context, final Account account) {
+        if (account.isEnabled()) {
+            registerPhoneAccount(context, account);
+        } else {
+            unregisterPhoneAccount(context, account);
+        }
+    }
+
+    private static void registerPhoneAccount(final Context context, final Account account) {
         try {
             registerPhoneAccountOrThrow(context, account);
         } catch (final IllegalArgumentException e) {
-            Toast.makeText(context, R.string.call_integration_not_available, Toast.LENGTH_LONG)
-                    .show();
+            Log.w(
+                    Config.LOGTAG,
+                    "could not register phone account for " + account.getJid().asBareJid(),
+                    e);
+            ContextCompat.getMainExecutor(context)
+                    .execute(() -> showCallIntegrationNotAvailable(context));
         }
     }
 
-    public static void registerPhoneAccountOrThrow(final Context context, final Account account) {
+    private static void showCallIntegrationNotAvailable(final Context context) {
+        Toast.makeText(context, R.string.call_integration_not_available, Toast.LENGTH_LONG).show();
+    }
+
+    private static void registerPhoneAccountOrThrow(final Context context, final Account account) {
         final var handle = getHandle(context, account);
         final var telecomManager = context.getSystemService(TelecomManager.class);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -242,24 +268,39 @@ public class CallIntegrationConnectionService extends ConnectionService {
         telecomManager.registerPhoneAccount(phoneAccount);
     }
 
-    public static void registerPhoneAccounts(
+    public static void togglePhoneAccountsAsync(
+            final Context context, final Collection<Account> accounts) {
+        ACCOUNT_REGISTRATION_EXECUTOR.execute(() -> togglePhoneAccounts(context, accounts));
+    }
+
+    private static void togglePhoneAccounts(
             final Context context, final Collection<Account> accounts) {
         for (final Account account : accounts) {
-            try {
-                registerPhoneAccountOrThrow(context, account);
-            } catch (final IllegalArgumentException e) {
-                Log.w(
-                        Config.LOGTAG,
-                        "could not register phone account for " + account.getJid().asBareJid(),
-                        e);
-                return;
+            if (account.isEnabled()) {
+                try {
+                    registerPhoneAccountOrThrow(context, account);
+                } catch (final IllegalArgumentException e) {
+                    Log.w(
+                            Config.LOGTAG,
+                            "could not register phone account for " + account.getJid().asBareJid(),
+                            e);
+                }
+            } else {
+                unregisterPhoneAccount(context, account);
             }
         }
     }
 
     public static void unregisterPhoneAccount(final Context context, final Account account) {
-        context.getSystemService(TelecomManager.class)
-                .unregisterPhoneAccount(getHandle(context, account));
+        final var handle = getHandle(context, account);
+        final var telecomManager = context.getSystemService(TelecomManager.class);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            if (telecomManager.getOwnSelfManagedPhoneAccounts().contains(handle)) {
+                telecomManager.unregisterPhoneAccount(handle);
+            }
+        } else {
+            telecomManager.unregisterPhoneAccount(handle);
+        }
     }
 
     public static PhoneAccountHandle getHandle(final Context context, final Account account) {
@@ -288,8 +329,13 @@ public class CallIntegrationConnectionService extends ConnectionService {
                         .show();
                 return;
             }
-            service.getSystemService(TelecomManager.class)
-                    .placeCall(CallIntegration.address(with), extras);
+            try {
+                service.getSystemService(TelecomManager.class)
+                        .placeCall(CallIntegration.address(with), extras);
+            } catch (final SecurityException e) {
+                Toast.makeText(service, R.string.call_integration_not_available, Toast.LENGTH_LONG)
+                        .show();
+            }
         } else {
             final var connection = createOutgoingRtpConnection(service, account, with, media);
             if (connection != null) {
@@ -319,8 +365,15 @@ public class CallIntegrationConnectionService extends ConnectionService {
         final var extras = new Bundle();
         extras.putString("sid", id.sessionId);
         bundle.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
-        context.getSystemService(TelecomManager.class)
-                .addNewIncomingCall(phoneAccountHandle, bundle);
+        try {
+            context.getSystemService(TelecomManager.class)
+                    .addNewIncomingCall(phoneAccountHandle, bundle);
+        } catch (final SecurityException e) {
+            Log.e(
+                    Config.LOGTAG,
+                    id.account.getJid().asBareJid() + ": call integration not available",
+                    e);
+        }
     }
 
     public static class ServiceConnectionService {

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -40,8 +40,6 @@ import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.security.KeyChain;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -1284,15 +1282,13 @@ public class XmppConnectionService extends Service {
         toggleSetProfilePictureActivity(hasEnabledAccounts);
         reconfigurePushDistributor();
 
-        CallIntegrationConnectionService.registerPhoneAccounts(this, this.accounts);
+        CallIntegrationConnectionService.togglePhoneAccountsAsync(this, this.accounts);
 
         restoreFromDatabase();
 
         if (QuickConversationsService.isContactListIntegration(this)
-                && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
-                        || ContextCompat.checkSelfPermission(
-                                        this, Manifest.permission.READ_CONTACTS)
-                                == PackageManager.PERMISSION_GRANTED)) {
+                && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
+                        == PackageManager.PERMISSION_GRANTED) {
             startContactObserver();
         }
         FILE_OBSERVER_EXECUTOR.execute(fileBackend::deleteHistoricAvatarPath);
@@ -2465,7 +2461,7 @@ public class XmppConnectionService extends Service {
     public void createAccount(final Account account) {
         account.initAccountServices(this);
         databaseBackend.createAccount(account);
-        CallIntegrationConnectionService.registerPhoneAccount(this, account);
+        CallIntegrationConnectionService.togglePhoneAccountAsync(this, account);
         this.accounts.add(account);
         this.reconnectAccountInBackground(account);
         updateAccountUi();
@@ -2589,6 +2585,7 @@ public class XmppConnectionService extends Service {
             toggleForegroundService();
             syncEnabledAccountSetting();
             mChannelDiscoveryService.cleanCache();
+            CallIntegrationConnectionService.togglePhoneAccountAsync(this, account);
             return true;
         } else {
             return false;