Loading src/com/android/server/telecom/PhoneAccountRegistrar.java +106 −8 Original line number Diff line number Diff line Loading @@ -59,7 +59,6 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; Loading @@ -76,8 +75,12 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim Loading Loading @@ -243,6 +246,46 @@ public class PhoneAccountRegistrar { return null; } /** * @return The {@link DefaultPhoneAccountHandle} containing the user-selected default calling * account and group Id for the {@link UserHandle} specified. */ private DefaultPhoneAccountHandle getUserSelectedDefaultPhoneAccount(UserHandle userHandle) { if (userHandle == null) { return null; } DefaultPhoneAccountHandle defaultPhoneAccountHandle = mState.defaultOutgoingAccountHandles .get(userHandle); if (defaultPhoneAccountHandle == null) { return null; } return defaultPhoneAccountHandle; } /** * @return The currently registered PhoneAccount in Telecom that has the same group Id. */ private PhoneAccount getPhoneAccountByGroupId(String groupId, ComponentName groupComponentName, UserHandle userHandle, PhoneAccountHandle excludePhoneAccountHandle) { if (groupId == null || groupId.isEmpty() || userHandle == null) { return null; } // Get the PhoneAccount with the same group Id (and same ComponentName) that is not the // newAccount that was just added List<PhoneAccount> accounts = getAllPhoneAccounts(userHandle).stream() .filter(account -> groupId.equals(account.getGroupId()) && !account.getAccountHandle().equals(excludePhoneAccountHandle) && Objects.equals(account.getAccountHandle().getComponentName(), groupComponentName)) .collect(Collectors.toList()); // There should be one or no PhoneAccounts with the same group Id if (accounts.size() > 1) { Log.w(this, "Found multiple PhoneAccounts registered to the same Group Id!"); } return accounts.isEmpty() ? null : accounts.get(0); } /** * Sets the phone account with which to place all calls by default. Set by the user * within phone settings. Loading Loading @@ -277,7 +320,8 @@ public class PhoneAccountRegistrar { } mState.defaultOutgoingAccountHandles .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle)); .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle, account.getGroupId())); } write(); Loading Loading @@ -591,6 +635,8 @@ public class PhoneAccountRegistrar { } mState.accounts.add(account); // Set defaults and replace based on the group Id. maybeReplaceOldAccount(account); // Reset enabled state to whatever the value was if the account was already registered, // or _true_ if this is a SIM-based account. All SIM-based accounts are always enabled. account.setIsEnabled( Loading Loading @@ -697,6 +743,40 @@ public class PhoneAccountRegistrar { } } private void maybeReplaceOldAccount(PhoneAccount newAccount) { UserHandle newAccountUserHandle = newAccount.getAccountHandle().getUserHandle(); DefaultPhoneAccountHandle defaultHandle = getUserSelectedDefaultPhoneAccount(newAccountUserHandle); if (defaultHandle == null || defaultHandle.groupId.isEmpty()) { Log.v(this, "maybeReplaceOldAccount: Not replacing PhoneAccount, no group Id or " + "default."); return; } if (!defaultHandle.groupId.equals(newAccount.getGroupId())) { Log.v(this, "maybeReplaceOldAccount: group Ids are not equal."); return; } if (Objects.equals(newAccount.getAccountHandle().getComponentName(), defaultHandle.phoneAccountHandle.getComponentName())) { // Move default calling account over to new user, since the ComponentNames and Group Ids // are the same. setUserSelectedOutgoingPhoneAccount(newAccount.getAccountHandle(), newAccountUserHandle); } else { Log.v(this, "maybeReplaceOldAccount: group Ids are equal, but ComponentName is not" + " the same as the default. Not replacing default PhoneAccount."); } PhoneAccount replacementAccount = getPhoneAccountByGroupId(newAccount.getGroupId(), newAccount.getAccountHandle().getComponentName(), newAccountUserHandle, newAccount.getAccountHandle()); if (replacementAccount != null) { // Unregister the old PhoneAccount. Log.v(this, "maybeReplaceOldAccount: Unregistering old PhoneAccount: " + replacementAccount.getAccountHandle()); unregisterPhoneAccount(replacementAccount.getAccountHandle()); } } /** * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission. Loading Loading @@ -904,10 +984,13 @@ public class PhoneAccountRegistrar { public final PhoneAccountHandle phoneAccountHandle; public final String groupId; public DefaultPhoneAccountHandle(UserHandle userHandle, PhoneAccountHandle phoneAccountHandle) { PhoneAccountHandle phoneAccountHandle, String groupId) { this.userHandle = userHandle; this.phoneAccountHandle = phoneAccountHandle; this.groupId = groupId; } } Loading Loading @@ -1154,6 +1237,13 @@ public class PhoneAccountRegistrar { serializer.endTag(null, tagName); } protected void writeNonNullString(String tagName, String value, XmlSerializer serializer) throws IOException { serializer.startTag(null, tagName); serializer.text(value != null ? value : ""); serializer.endTag(null, tagName); } /** * Reads a string array from the XML parser. * Loading Loading @@ -1292,8 +1382,9 @@ public class PhoneAccountRegistrar { while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (parser.getName().equals(DEFAULT_OUTGOING)) { if (s.versionNumber < 9) { // Migration old default phone account handle here by assuming the // default phone account handle is belong to primary user. // Migrate old default phone account handle here by assuming the // default phone account handle belongs to the primary user. Also, // assume there are no groups. parser.nextTag(); PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleXml .readFromXml(parser, s.versionNumber, context); Loading @@ -1303,7 +1394,7 @@ public class PhoneAccountRegistrar { UserHandle userHandle = primaryUser.getUserHandle(); DefaultPhoneAccountHandle defaultPhoneAccountHandle = new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle); phoneAccountHandle, "" /* groupId */); s.defaultOutgoingAccountHandles .put(userHandle, defaultPhoneAccountHandle); } Loading Loading @@ -1343,6 +1434,7 @@ public class PhoneAccountRegistrar { private static final String CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE = "default_outgoing_phone_account_handle"; private static final String USER_SERIAL_NUMBER = "user_serial_number"; private static final String GROUP_ID = "group_id"; private static final String ACCOUNT_HANDLE = "account_handle"; @Override Loading @@ -1354,6 +1446,7 @@ public class PhoneAccountRegistrar { if (serialNumber != -1) { serializer.startTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE); writeLong(USER_SERIAL_NUMBER, serialNumber, serializer); writeNonNullString(GROUP_ID, o.groupId, serializer); serializer.startTag(null, ACCOUNT_HANDLE); sPhoneAccountHandleXml.writeToXml(o.phoneAccountHandle, serializer, context); Loading @@ -1371,6 +1464,7 @@ public class PhoneAccountRegistrar { int outerDepth = parser.getDepth(); PhoneAccountHandle accountHandle = null; String userSerialNumberString = null; String groupId = ""; while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (parser.getName().equals(ACCOUNT_HANDLE)) { parser.nextTag(); Loading @@ -1379,6 +1473,9 @@ public class PhoneAccountRegistrar { } else if (parser.getName().equals(USER_SERIAL_NUMBER)) { parser.next(); userSerialNumberString = parser.getText(); } else if (parser.getName().equals(GROUP_ID)) { parser.next(); groupId = parser.getText(); } } UserHandle userHandle = null; Loading @@ -1392,8 +1489,9 @@ public class PhoneAccountRegistrar { "Could not parse UserHandle " + userSerialNumberString); } } if (accountHandle != null && userHandle != null) { return new DefaultPhoneAccountHandle(userHandle, accountHandle); if (accountHandle != null && userHandle != null && groupId != null) { return new DefaultPhoneAccountHandle(userHandle, accountHandle, groupId); } } return null; Loading tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +234 −9 Original line number Diff line number Diff line Loading @@ -171,8 +171,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { public void testAccounts() throws Exception { int i = 0; mComponentContextFixture.addConnectionService( makeQuickConnectionServiceComponentName(), mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++) Loading Loading @@ -205,8 +204,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { @MediumTest public void testDefaultOutgoing() throws Exception { mComponentContextFixture.addConnectionService( makeQuickConnectionServiceComponentName(), mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) Loading Loading @@ -252,6 +250,229 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); } @MediumTest public void testReplacePhoneAccountByGroup() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Add call capable SIP account, make sure tel: doesn't change PhoneAccountHandle sipAccount = makeQuickAccountHandle("sip_acct"); registerAndEnableAccount(new PhoneAccount.Builder(sipAccount, "sip_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Replace tel: account with another in the same Group PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL); assertEquals(telAccount2, defaultAccount); assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); } @MediumTest public void testAddSameDefault() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); mRegistrar.unregisterPhoneAccount(telAccount1); // Register Emergency Account and unregister PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct"); registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertNull(defaultAccount); mRegistrar.unregisterPhoneAccount(emerAccount); // Re-register the same account and make sure the default is in place registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); } @MediumTest public void testAddSameGroup() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); mRegistrar.unregisterPhoneAccount(telAccount1); // Register Emergency Account and unregister PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct"); registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertNull(defaultAccount); mRegistrar.unregisterPhoneAccount(emerAccount); // Re-register a new account with the same group PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount2, defaultAccount); } @MediumTest public void testAddSameGroupButDifferentComponent() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); // Register a new account with the same group, but different Component, so don't replace // Default PhoneAccountHandle telAccount2 = makeQuickAccountHandle( new ComponentName("other1", "other2"), "tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount2)); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); } @MediumTest public void testAddSameGroupButDifferentComponent2() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL)); // Register first tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle( new ComponentName("other1", "other2"), "tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); // Register second account with the same group, but a second Component, so don't replace // Default PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); PhoneAccountHandle defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Register third account with the second component name, but same group id PhoneAccountHandle telAccount3 = makeQuickAccountHandle("tel_acct3"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount3, "tel_acct3") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); // Make sure that the default account is still the original PhoneAccount and that the // second PhoneAccount with the second ComponentName was replaced by the third PhoneAccount defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount2)); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount3)); } @MediumTest public void testPhoneAccountParceling() throws Exception { PhoneAccountHandle handle = makeQuickAccountHandle("foo"); Loading @@ -268,6 +489,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { .setShortDescription("short description") .setSubscriptionAddress(Uri.parse("tel:2345678")) .setSupportedUriSchemes(Arrays.asList("tel", "sip")) .setGroupId("testGroup") .build()); roundTripPhoneAccount( new PhoneAccount.Builder(handle, "foo") Loading @@ -281,6 +503,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { .setShortDescription("short description") .setSubscriptionAddress(Uri.parse("tel:2345678")) .setSupportedUriSchemes(Arrays.asList("tel", "sip")) .setGroupId("testGroup") .build()); } Loading @@ -291,10 +514,11 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { } private static PhoneAccountHandle makeQuickAccountHandle(String id) { return new PhoneAccountHandle( makeQuickConnectionServiceComponentName(), id, Process.myUserHandle()); return makeQuickAccountHandle(makeQuickConnectionServiceComponentName(), id); } private static PhoneAccountHandle makeQuickAccountHandle(ComponentName name, String id) { return new PhoneAccountHandle(name, id, Process.myUserHandle()); } private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) { Loading Loading @@ -457,7 +681,8 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { new ComponentName("pkg0", "cls0"), "id0"); UserHandle userHandle = phoneAccountHandle.getUserHandle(); s.defaultOutgoingAccountHandles .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle)); .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle, "testGroup")); return s; } } Loading
src/com/android/server/telecom/PhoneAccountRegistrar.java +106 −8 Original line number Diff line number Diff line Loading @@ -59,7 +59,6 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; Loading @@ -76,8 +75,12 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim Loading Loading @@ -243,6 +246,46 @@ public class PhoneAccountRegistrar { return null; } /** * @return The {@link DefaultPhoneAccountHandle} containing the user-selected default calling * account and group Id for the {@link UserHandle} specified. */ private DefaultPhoneAccountHandle getUserSelectedDefaultPhoneAccount(UserHandle userHandle) { if (userHandle == null) { return null; } DefaultPhoneAccountHandle defaultPhoneAccountHandle = mState.defaultOutgoingAccountHandles .get(userHandle); if (defaultPhoneAccountHandle == null) { return null; } return defaultPhoneAccountHandle; } /** * @return The currently registered PhoneAccount in Telecom that has the same group Id. */ private PhoneAccount getPhoneAccountByGroupId(String groupId, ComponentName groupComponentName, UserHandle userHandle, PhoneAccountHandle excludePhoneAccountHandle) { if (groupId == null || groupId.isEmpty() || userHandle == null) { return null; } // Get the PhoneAccount with the same group Id (and same ComponentName) that is not the // newAccount that was just added List<PhoneAccount> accounts = getAllPhoneAccounts(userHandle).stream() .filter(account -> groupId.equals(account.getGroupId()) && !account.getAccountHandle().equals(excludePhoneAccountHandle) && Objects.equals(account.getAccountHandle().getComponentName(), groupComponentName)) .collect(Collectors.toList()); // There should be one or no PhoneAccounts with the same group Id if (accounts.size() > 1) { Log.w(this, "Found multiple PhoneAccounts registered to the same Group Id!"); } return accounts.isEmpty() ? null : accounts.get(0); } /** * Sets the phone account with which to place all calls by default. Set by the user * within phone settings. Loading Loading @@ -277,7 +320,8 @@ public class PhoneAccountRegistrar { } mState.defaultOutgoingAccountHandles .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle)); .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle, account.getGroupId())); } write(); Loading Loading @@ -591,6 +635,8 @@ public class PhoneAccountRegistrar { } mState.accounts.add(account); // Set defaults and replace based on the group Id. maybeReplaceOldAccount(account); // Reset enabled state to whatever the value was if the account was already registered, // or _true_ if this is a SIM-based account. All SIM-based accounts are always enabled. account.setIsEnabled( Loading Loading @@ -697,6 +743,40 @@ public class PhoneAccountRegistrar { } } private void maybeReplaceOldAccount(PhoneAccount newAccount) { UserHandle newAccountUserHandle = newAccount.getAccountHandle().getUserHandle(); DefaultPhoneAccountHandle defaultHandle = getUserSelectedDefaultPhoneAccount(newAccountUserHandle); if (defaultHandle == null || defaultHandle.groupId.isEmpty()) { Log.v(this, "maybeReplaceOldAccount: Not replacing PhoneAccount, no group Id or " + "default."); return; } if (!defaultHandle.groupId.equals(newAccount.getGroupId())) { Log.v(this, "maybeReplaceOldAccount: group Ids are not equal."); return; } if (Objects.equals(newAccount.getAccountHandle().getComponentName(), defaultHandle.phoneAccountHandle.getComponentName())) { // Move default calling account over to new user, since the ComponentNames and Group Ids // are the same. setUserSelectedOutgoingPhoneAccount(newAccount.getAccountHandle(), newAccountUserHandle); } else { Log.v(this, "maybeReplaceOldAccount: group Ids are equal, but ComponentName is not" + " the same as the default. Not replacing default PhoneAccount."); } PhoneAccount replacementAccount = getPhoneAccountByGroupId(newAccount.getGroupId(), newAccount.getAccountHandle().getComponentName(), newAccountUserHandle, newAccount.getAccountHandle()); if (replacementAccount != null) { // Unregister the old PhoneAccount. Log.v(this, "maybeReplaceOldAccount: Unregistering old PhoneAccount: " + replacementAccount.getAccountHandle()); unregisterPhoneAccount(replacementAccount.getAccountHandle()); } } /** * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission. Loading Loading @@ -904,10 +984,13 @@ public class PhoneAccountRegistrar { public final PhoneAccountHandle phoneAccountHandle; public final String groupId; public DefaultPhoneAccountHandle(UserHandle userHandle, PhoneAccountHandle phoneAccountHandle) { PhoneAccountHandle phoneAccountHandle, String groupId) { this.userHandle = userHandle; this.phoneAccountHandle = phoneAccountHandle; this.groupId = groupId; } } Loading Loading @@ -1154,6 +1237,13 @@ public class PhoneAccountRegistrar { serializer.endTag(null, tagName); } protected void writeNonNullString(String tagName, String value, XmlSerializer serializer) throws IOException { serializer.startTag(null, tagName); serializer.text(value != null ? value : ""); serializer.endTag(null, tagName); } /** * Reads a string array from the XML parser. * Loading Loading @@ -1292,8 +1382,9 @@ public class PhoneAccountRegistrar { while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (parser.getName().equals(DEFAULT_OUTGOING)) { if (s.versionNumber < 9) { // Migration old default phone account handle here by assuming the // default phone account handle is belong to primary user. // Migrate old default phone account handle here by assuming the // default phone account handle belongs to the primary user. Also, // assume there are no groups. parser.nextTag(); PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleXml .readFromXml(parser, s.versionNumber, context); Loading @@ -1303,7 +1394,7 @@ public class PhoneAccountRegistrar { UserHandle userHandle = primaryUser.getUserHandle(); DefaultPhoneAccountHandle defaultPhoneAccountHandle = new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle); phoneAccountHandle, "" /* groupId */); s.defaultOutgoingAccountHandles .put(userHandle, defaultPhoneAccountHandle); } Loading Loading @@ -1343,6 +1434,7 @@ public class PhoneAccountRegistrar { private static final String CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE = "default_outgoing_phone_account_handle"; private static final String USER_SERIAL_NUMBER = "user_serial_number"; private static final String GROUP_ID = "group_id"; private static final String ACCOUNT_HANDLE = "account_handle"; @Override Loading @@ -1354,6 +1446,7 @@ public class PhoneAccountRegistrar { if (serialNumber != -1) { serializer.startTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE); writeLong(USER_SERIAL_NUMBER, serialNumber, serializer); writeNonNullString(GROUP_ID, o.groupId, serializer); serializer.startTag(null, ACCOUNT_HANDLE); sPhoneAccountHandleXml.writeToXml(o.phoneAccountHandle, serializer, context); Loading @@ -1371,6 +1464,7 @@ public class PhoneAccountRegistrar { int outerDepth = parser.getDepth(); PhoneAccountHandle accountHandle = null; String userSerialNumberString = null; String groupId = ""; while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (parser.getName().equals(ACCOUNT_HANDLE)) { parser.nextTag(); Loading @@ -1379,6 +1473,9 @@ public class PhoneAccountRegistrar { } else if (parser.getName().equals(USER_SERIAL_NUMBER)) { parser.next(); userSerialNumberString = parser.getText(); } else if (parser.getName().equals(GROUP_ID)) { parser.next(); groupId = parser.getText(); } } UserHandle userHandle = null; Loading @@ -1392,8 +1489,9 @@ public class PhoneAccountRegistrar { "Could not parse UserHandle " + userSerialNumberString); } } if (accountHandle != null && userHandle != null) { return new DefaultPhoneAccountHandle(userHandle, accountHandle); if (accountHandle != null && userHandle != null && groupId != null) { return new DefaultPhoneAccountHandle(userHandle, accountHandle, groupId); } } return null; Loading
tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +234 −9 Original line number Diff line number Diff line Loading @@ -171,8 +171,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { public void testAccounts() throws Exception { int i = 0; mComponentContextFixture.addConnectionService( makeQuickConnectionServiceComponentName(), mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++) Loading Loading @@ -205,8 +204,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { @MediumTest public void testDefaultOutgoing() throws Exception { mComponentContextFixture.addConnectionService( makeQuickConnectionServiceComponentName(), mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) Loading Loading @@ -252,6 +250,229 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); } @MediumTest public void testReplacePhoneAccountByGroup() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Add call capable SIP account, make sure tel: doesn't change PhoneAccountHandle sipAccount = makeQuickAccountHandle("sip_acct"); registerAndEnableAccount(new PhoneAccount.Builder(sipAccount, "sip_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Replace tel: account with another in the same Group PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL); assertEquals(telAccount2, defaultAccount); assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); } @MediumTest public void testAddSameDefault() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); mRegistrar.unregisterPhoneAccount(telAccount1); // Register Emergency Account and unregister PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct"); registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertNull(defaultAccount); mRegistrar.unregisterPhoneAccount(emerAccount); // Re-register the same account and make sure the default is in place registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); } @MediumTest public void testAddSameGroup() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull( mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); mRegistrar.unregisterPhoneAccount(telAccount1); // Register Emergency Account and unregister PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct"); registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertNull(defaultAccount); mRegistrar.unregisterPhoneAccount(emerAccount); // Re-register a new account with the same group PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount2, defaultAccount); } @MediumTest public void testAddSameGroupButDifferentComponent() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL)); // Register one tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); PhoneAccountHandle defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); // Register a new account with the same group, but different Component, so don't replace // Default PhoneAccountHandle telAccount2 = makeQuickAccountHandle( new ComponentName("other1", "other2"), "tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount2)); defaultAccount = mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle()); assertEquals(telAccount1, defaultAccount); } @MediumTest public void testAddSameGroupButDifferentComponent2() throws Exception { mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class)); // By default, there is no default outgoing account (nothing has been registered) assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( PhoneAccount.SCHEME_TEL)); // Register first tel: account PhoneAccountHandle telAccount1 = makeQuickAccountHandle( new ComponentName("other1", "other2"), "tel_acct1"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle()); // Register second account with the same group, but a second Component, so don't replace // Default PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); PhoneAccountHandle defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); // Register third account with the second component name, but same group id PhoneAccountHandle telAccount3 = makeQuickAccountHandle("tel_acct3"); registerAndEnableAccount(new PhoneAccount.Builder(telAccount3, "tel_acct3") .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .setGroupId("testGroup") .build()); // Make sure that the default account is still the original PhoneAccount and that the // second PhoneAccount with the second ComponentName was replaced by the third PhoneAccount defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL); assertEquals(telAccount1, defaultAccount); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1)); assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount2)); assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount3)); } @MediumTest public void testPhoneAccountParceling() throws Exception { PhoneAccountHandle handle = makeQuickAccountHandle("foo"); Loading @@ -268,6 +489,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { .setShortDescription("short description") .setSubscriptionAddress(Uri.parse("tel:2345678")) .setSupportedUriSchemes(Arrays.asList("tel", "sip")) .setGroupId("testGroup") .build()); roundTripPhoneAccount( new PhoneAccount.Builder(handle, "foo") Loading @@ -281,6 +503,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { .setShortDescription("short description") .setSubscriptionAddress(Uri.parse("tel:2345678")) .setSupportedUriSchemes(Arrays.asList("tel", "sip")) .setGroupId("testGroup") .build()); } Loading @@ -291,10 +514,11 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { } private static PhoneAccountHandle makeQuickAccountHandle(String id) { return new PhoneAccountHandle( makeQuickConnectionServiceComponentName(), id, Process.myUserHandle()); return makeQuickAccountHandle(makeQuickConnectionServiceComponentName(), id); } private static PhoneAccountHandle makeQuickAccountHandle(ComponentName name, String id) { return new PhoneAccountHandle(name, id, Process.myUserHandle()); } private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) { Loading Loading @@ -457,7 +681,8 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { new ComponentName("pkg0", "cls0"), "id0"); UserHandle userHandle = phoneAccountHandle.getUserHandle(); s.defaultOutgoingAccountHandles .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle)); .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle, "testGroup")); return s; } }