1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.telecom.tests;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.graphics.BitmapFactory;
22import android.graphics.Rect;
23import android.graphics.drawable.Icon;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Process;
28import android.os.UserHandle;
29import android.telecom.Log;
30import android.telecom.PhoneAccount;
31import android.telecom.PhoneAccountHandle;
32import android.telecom.TelecomManager;
33import android.test.suitebuilder.annotation.MediumTest;
34import android.util.Xml;
35
36import com.android.internal.telecom.IConnectionService;
37import com.android.internal.util.FastXmlSerializer;
38import com.android.server.telecom.DefaultDialerCache;
39import com.android.server.telecom.PhoneAccountRegistrar;
40import com.android.server.telecom.PhoneAccountRegistrar.DefaultPhoneAccountHandle;
41
42import org.mockito.Mock;
43import org.mockito.Mockito;
44import org.mockito.MockitoAnnotations;
45import org.xmlpull.v1.XmlPullParser;
46import org.xmlpull.v1.XmlSerializer;
47
48import java.io.BufferedInputStream;
49import java.io.BufferedOutputStream;
50import java.io.ByteArrayInputStream;
51import java.io.ByteArrayOutputStream;
52import java.io.File;
53import java.util.Arrays;
54import java.util.Set;
55
56import static org.mockito.Matchers.anyInt;
57import static org.mockito.Matchers.anyString;
58import static org.mockito.Mockito.when;
59
60public class PhoneAccountRegistrarTest extends TelecomTestCase {
61
62    private static final int MAX_VERSION = Integer.MAX_VALUE;
63    private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
64    private static final String TEST_LABEL = "right";
65    private PhoneAccountRegistrar mRegistrar;
66    @Mock private TelecomManager mTelecomManager;
67    @Mock private DefaultDialerCache mDefaultDialerCache;
68    @Mock private PhoneAccountRegistrar.AppLabelProxy mAppLabelProxy;
69
70    @Override
71    public void setUp() throws Exception {
72        super.setUp();
73        MockitoAnnotations.initMocks(this);
74        mComponentContextFixture.setTelecomManager(mTelecomManager);
75        new File(
76                mComponentContextFixture.getTestDouble().getApplicationContext().getFilesDir(),
77                FILE_NAME)
78                .delete();
79        when(mDefaultDialerCache.getDefaultDialerApplication(anyInt()))
80                .thenReturn("com.android.dialer");
81        when(mAppLabelProxy.getAppLabel(anyString()))
82                .thenReturn(TEST_LABEL);
83        mRegistrar = new PhoneAccountRegistrar(
84                mComponentContextFixture.getTestDouble().getApplicationContext(),
85                FILE_NAME, mDefaultDialerCache, mAppLabelProxy);
86    }
87
88    @Override
89    public void tearDown() throws Exception {
90        mRegistrar = null;
91        new File(
92                mComponentContextFixture.getTestDouble().getApplicationContext().getFilesDir(),
93                FILE_NAME)
94                .delete();
95        super.tearDown();
96    }
97
98    @MediumTest
99    public void testPhoneAccountHandle() throws Exception {
100        PhoneAccountHandle input = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), "id0");
101        PhoneAccountHandle result = roundTripXml(this, input,
102                PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
103        assertPhoneAccountHandleEquals(input, result);
104
105        PhoneAccountHandle inputN = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), null);
106        PhoneAccountHandle resultN = roundTripXml(this, inputN,
107                PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
108        Log.i(this, "inputN = %s, resultN = %s", inputN, resultN);
109        assertPhoneAccountHandleEquals(inputN, resultN);
110    }
111
112    @MediumTest
113    public void testPhoneAccount() throws Exception {
114        Bundle testBundle = new Bundle();
115        testBundle.putInt("EXTRA_INT_1", 1);
116        testBundle.putInt("EXTRA_INT_100", 100);
117        testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
118        testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
119        testBundle.putString("EXTRA_STR1", "Hello");
120        testBundle.putString("EXTRA_STR2", "There");
121
122        PhoneAccount input = makeQuickAccountBuilder("id0", 0)
123                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
124                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
125                .setExtras(testBundle)
126                .setIsEnabled(true)
127                .build();
128        PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
129                mContext);
130
131        assertPhoneAccountEquals(input, result);
132    }
133
134    @MediumTest
135    public void testDefaultPhoneAccountHandleEmptyGroup() throws Exception {
136        DefaultPhoneAccountHandle input = new DefaultPhoneAccountHandle(Process.myUserHandle(),
137                makeQuickAccountHandle("i1"), "");
138        DefaultPhoneAccountHandle result = roundTripXml(this, input,
139                PhoneAccountRegistrar.sDefaultPhoneAcountHandleXml, mContext);
140
141        assertDefaultPhoneAccountHandleEquals(input, result);
142    }
143
144    /**
145     * Test to ensure non-supported values
146     * @throws Exception
147     */
148    @MediumTest
149    public void testPhoneAccountExtrasEdge() throws Exception {
150        Bundle testBundle = new Bundle();
151        // Ensure null values for string are not persisted.
152        testBundle.putString("EXTRA_STR2", null);
153        //
154
155        // Ensure unsupported data types are not persisted.
156        testBundle.putShort("EXTRA_SHORT", (short) 2);
157        testBundle.putByte("EXTRA_BYTE", (byte) 1);
158        testBundle.putParcelable("EXTRA_PARC", new Rect(1, 1, 1, 1));
159        // Put in something valid so the bundle exists.
160        testBundle.putString("EXTRA_OK", "OK");
161
162        PhoneAccount input = makeQuickAccountBuilder("id0", 0)
163                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
164                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
165                .setExtras(testBundle)
166                .build();
167        PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
168                mContext);
169
170        Bundle extras = result.getExtras();
171        assertFalse(extras.keySet().contains("EXTRA_STR2"));
172        assertFalse(extras.keySet().contains("EXTRA_SHORT"));
173        assertFalse(extras.keySet().contains("EXTRA_BYTE"));
174        assertFalse(extras.keySet().contains("EXTRA_PARC"));
175    }
176
177    @MediumTest
178    public void testState() throws Exception {
179        PhoneAccountRegistrar.State input = makeQuickState();
180        PhoneAccountRegistrar.State result = roundTripXml(this, input,
181                PhoneAccountRegistrar.sStateXml,
182                mContext);
183        assertStateEquals(input, result);
184    }
185
186    private void registerAndEnableAccount(PhoneAccount account) {
187        mRegistrar.registerPhoneAccount(account);
188        mRegistrar.enablePhoneAccount(account.getAccountHandle(), true);
189    }
190
191    @MediumTest
192    public void testAccounts() throws Exception {
193        int i = 0;
194
195        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
196                Mockito.mock(IConnectionService.class));
197
198        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
199                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
200                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
201                .build());
202        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
203                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
204                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
205                .build());
206        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
207                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
208                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
209                .build());
210        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
211                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
212                .build());
213
214        assertEquals(4, mRegistrar.getAllPhoneAccountsOfCurrentUser().size());
215        assertEquals(3, mRegistrar.getCallCapablePhoneAccountsOfCurrentUser(null, false).size());
216        assertEquals(null, mRegistrar.getSimCallManagerOfCurrentUser());
217        assertEquals(null, mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
218                PhoneAccount.SCHEME_TEL));
219    }
220
221    @MediumTest
222    public void testSimCallManager() throws Exception {
223        // TODO
224    }
225
226    @MediumTest
227    public void testDefaultOutgoing() throws Exception {
228        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
229                Mockito.mock(IConnectionService.class));
230
231        // By default, there is no default outgoing account (nothing has been registered)
232        assertNull(
233                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
234
235        // Register one tel: account
236        PhoneAccountHandle telAccount = makeQuickAccountHandle("tel_acct");
237        registerAndEnableAccount(new PhoneAccount.Builder(telAccount, "tel_acct")
238                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
239                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
240                .build());
241        PhoneAccountHandle defaultAccount =
242                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
243        assertEquals(telAccount, defaultAccount);
244
245        // Add a SIP account, make sure tel: doesn't change
246        PhoneAccountHandle sipAccount = makeQuickAccountHandle("sip_acct");
247        registerAndEnableAccount(new PhoneAccount.Builder(sipAccount, "sip_acct")
248                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
249                .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
250                .build());
251        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
252                PhoneAccount.SCHEME_SIP);
253        assertEquals(sipAccount, defaultAccount);
254        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
255                PhoneAccount.SCHEME_TEL);
256        assertEquals(telAccount, defaultAccount);
257
258        // Add a connection manager, make sure tel: doesn't change
259        PhoneAccountHandle connectionManager = makeQuickAccountHandle("mgr_acct");
260        registerAndEnableAccount(new PhoneAccount.Builder(connectionManager, "mgr_acct")
261                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
262                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
263                .build());
264        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
265                PhoneAccount.SCHEME_TEL);
266        assertEquals(telAccount, defaultAccount);
267
268        // Unregister the tel: account, make sure there is no tel: default now.
269        mRegistrar.unregisterPhoneAccount(telAccount);
270        assertNull(
271                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
272    }
273
274    @MediumTest
275    public void testReplacePhoneAccountByGroup() throws Exception {
276        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
277                Mockito.mock(IConnectionService.class));
278
279        // By default, there is no default outgoing account (nothing has been registered)
280        assertNull(
281                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
282
283        // Register one tel: account
284        PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1");
285        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
286                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
287                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
288                .setGroupId("testGroup")
289                .build());
290        mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle());
291        PhoneAccountHandle defaultAccount =
292                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
293        assertEquals(telAccount1, defaultAccount);
294
295        // Add call capable SIP account, make sure tel: doesn't change
296        PhoneAccountHandle sipAccount = makeQuickAccountHandle("sip_acct");
297        registerAndEnableAccount(new PhoneAccount.Builder(sipAccount, "sip_acct")
298                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
299                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
300                .build());
301        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
302                PhoneAccount.SCHEME_TEL);
303        assertEquals(telAccount1, defaultAccount);
304
305        // Replace tel: account with another in the same Group
306        PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2");
307        registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2")
308                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
309                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
310                .setGroupId("testGroup")
311                .build());
312        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
313                PhoneAccount.SCHEME_TEL);
314        assertEquals(telAccount2, defaultAccount);
315        assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount1));
316    }
317
318    @MediumTest
319    public void testAddSameDefault() throws Exception {
320        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
321                Mockito.mock(IConnectionService.class));
322
323        // By default, there is no default outgoing account (nothing has been registered)
324        assertNull(
325                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
326
327        // Register one tel: account
328        PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1");
329        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
330                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
331                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
332                .setGroupId("testGroup")
333                .build());
334        mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle());
335        PhoneAccountHandle defaultAccount =
336                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
337        assertEquals(telAccount1, defaultAccount);
338        mRegistrar.unregisterPhoneAccount(telAccount1);
339
340        // Register Emergency Account and unregister
341        PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct");
342        registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct")
343                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
344                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
345                .build());
346        defaultAccount =
347                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
348        assertNull(defaultAccount);
349        mRegistrar.unregisterPhoneAccount(emerAccount);
350
351        // Re-register the same account and make sure the default is in place
352        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
353                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
354                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
355                .setGroupId("testGroup")
356                .build());
357        defaultAccount =
358                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
359        assertEquals(telAccount1, defaultAccount);
360    }
361
362    @MediumTest
363    public void testAddSameGroup() throws Exception {
364        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
365                Mockito.mock(IConnectionService.class));
366
367        // By default, there is no default outgoing account (nothing has been registered)
368        assertNull(
369                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
370
371        // Register one tel: account
372        PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1");
373        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
374                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
375                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
376                .setGroupId("testGroup")
377                .build());
378        mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle());
379        PhoneAccountHandle defaultAccount =
380                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
381        assertEquals(telAccount1, defaultAccount);
382        mRegistrar.unregisterPhoneAccount(telAccount1);
383
384        // Register Emergency Account and unregister
385        PhoneAccountHandle emerAccount = makeQuickAccountHandle("emer_acct");
386        registerAndEnableAccount(new PhoneAccount.Builder(emerAccount, "emer_acct")
387                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
388                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
389                .build());
390        defaultAccount =
391                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
392        assertNull(defaultAccount);
393        mRegistrar.unregisterPhoneAccount(emerAccount);
394
395        // Re-register a new account with the same group
396        PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2");
397        registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2")
398                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
399                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
400                .setGroupId("testGroup")
401                .build());
402        defaultAccount =
403                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
404        assertEquals(telAccount2, defaultAccount);
405    }
406
407    @MediumTest
408    public void testAddSameGroupButDifferentComponent() throws Exception {
409        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
410                Mockito.mock(IConnectionService.class));
411
412        // By default, there is no default outgoing account (nothing has been registered)
413        assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
414                PhoneAccount.SCHEME_TEL));
415
416        // Register one tel: account
417        PhoneAccountHandle telAccount1 = makeQuickAccountHandle("tel_acct1");
418        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
419                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
420                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
421                .setGroupId("testGroup")
422                .build());
423        mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle());
424        PhoneAccountHandle defaultAccount =
425                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
426        assertEquals(telAccount1, defaultAccount);
427        assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1));
428
429        // Register a new account with the same group, but different Component, so don't replace
430        // Default
431        PhoneAccountHandle telAccount2 =  makeQuickAccountHandle(
432                new ComponentName("other1", "other2"), "tel_acct2");
433        registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2")
434                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
435                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
436                .setGroupId("testGroup")
437                .build());
438        assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount2));
439
440        defaultAccount =
441                mRegistrar.getUserSelectedOutgoingPhoneAccount(Process.myUserHandle());
442        assertEquals(telAccount1, defaultAccount);
443    }
444
445    @MediumTest
446    public void testAddSameGroupButDifferentComponent2() throws Exception {
447        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
448                Mockito.mock(IConnectionService.class));
449
450        // By default, there is no default outgoing account (nothing has been registered)
451        assertNull(mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
452                PhoneAccount.SCHEME_TEL));
453
454        // Register first tel: account
455        PhoneAccountHandle telAccount1 =  makeQuickAccountHandle(
456                new ComponentName("other1", "other2"), "tel_acct1");
457        registerAndEnableAccount(new PhoneAccount.Builder(telAccount1, "tel_acct1")
458                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
459                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
460                .setGroupId("testGroup")
461                .build());
462        assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1));
463        mRegistrar.setUserSelectedOutgoingPhoneAccount(telAccount1, Process.myUserHandle());
464
465        // Register second account with the same group, but a second Component, so don't replace
466        // Default
467        PhoneAccountHandle telAccount2 = makeQuickAccountHandle("tel_acct2");
468        registerAndEnableAccount(new PhoneAccount.Builder(telAccount2, "tel_acct2")
469                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
470                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
471                .setGroupId("testGroup")
472                .build());
473
474        PhoneAccountHandle defaultAccount =
475                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
476        assertEquals(telAccount1, defaultAccount);
477
478        // Register third account with the second component name, but same group id
479        PhoneAccountHandle telAccount3 = makeQuickAccountHandle("tel_acct3");
480        registerAndEnableAccount(new PhoneAccount.Builder(telAccount3, "tel_acct3")
481                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
482                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
483                .setGroupId("testGroup")
484                .build());
485
486        // Make sure that the default account is still the original PhoneAccount and that the
487        // second PhoneAccount with the second ComponentName was replaced by the third PhoneAccount
488        defaultAccount =
489                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
490        assertEquals(telAccount1, defaultAccount);
491
492        assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount1));
493        assertNull(mRegistrar.getPhoneAccountUnchecked(telAccount2));
494        assertNotNull(mRegistrar.getPhoneAccountUnchecked(telAccount3));
495    }
496
497    @MediumTest
498    public void testPhoneAccountParceling() throws Exception {
499        PhoneAccountHandle handle = makeQuickAccountHandle("foo");
500        roundTripPhoneAccount(new PhoneAccount.Builder(handle, null).build());
501        roundTripPhoneAccount(new PhoneAccount.Builder(handle, "foo").build());
502        roundTripPhoneAccount(
503                new PhoneAccount.Builder(handle, "foo")
504                        .setAddress(Uri.parse("tel:123456"))
505                        .setCapabilities(23)
506                        .setHighlightColor(0xf0f0f0)
507                        .setIcon(Icon.createWithResource(
508                                "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
509                        // TODO: set icon tint (0xfefefe)
510                        .setShortDescription("short description")
511                        .setSubscriptionAddress(Uri.parse("tel:2345678"))
512                        .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
513                        .setGroupId("testGroup")
514                        .build());
515        roundTripPhoneAccount(
516                new PhoneAccount.Builder(handle, "foo")
517                        .setAddress(Uri.parse("tel:123456"))
518                        .setCapabilities(23)
519                        .setHighlightColor(0xf0f0f0)
520                        .setIcon(Icon.createWithBitmap(
521                                BitmapFactory.decodeResource(
522                                        getContext().getResources(),
523                                        R.drawable.stat_sys_phone_call)))
524                        .setShortDescription("short description")
525                        .setSubscriptionAddress(Uri.parse("tel:2345678"))
526                        .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
527                        .setGroupId("testGroup")
528                        .build());
529    }
530
531    /**
532     * Tests ability to register a self-managed PhoneAccount; verifies that the user defined label
533     * is overridden.
534     * @throws Exception
535     */
536    @MediumTest
537    public void testSelfManagedPhoneAccount() throws Exception {
538        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
539                Mockito.mock(IConnectionService.class));
540
541        PhoneAccountHandle selfManagedHandle =  makeQuickAccountHandle(
542                new ComponentName("self", "managed"), "selfie1");
543
544        PhoneAccount selfManagedAccount = new PhoneAccount.Builder(selfManagedHandle, "Wrong")
545                .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
546                .build();
547
548        mRegistrar.registerPhoneAccount(selfManagedAccount);
549
550        PhoneAccount registeredAccount = mRegistrar.getPhoneAccountUnchecked(selfManagedHandle);
551        assertEquals(TEST_LABEL, registeredAccount.getLabel());
552    }
553
554    /**
555     * Tests to ensure that when registering a self-managed PhoneAccount, it cannot also be defined
556     * as a call provider, connection manager, or sim subscription.
557     * @throws Exception
558     */
559    @MediumTest
560    public void testSelfManagedCapabilityOverride() throws Exception {
561        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
562                Mockito.mock(IConnectionService.class));
563
564        PhoneAccountHandle selfManagedHandle =  makeQuickAccountHandle(
565                new ComponentName("self", "managed"), "selfie1");
566
567        PhoneAccount selfManagedAccount = new PhoneAccount.Builder(selfManagedHandle, TEST_LABEL)
568                .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
569                        PhoneAccount.CAPABILITY_CALL_PROVIDER |
570                        PhoneAccount.CAPABILITY_CONNECTION_MANAGER |
571                        PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
572                .build();
573
574        mRegistrar.registerPhoneAccount(selfManagedAccount);
575
576        PhoneAccount registeredAccount = mRegistrar.getPhoneAccountUnchecked(selfManagedHandle);
577        assertEquals(PhoneAccount.CAPABILITY_SELF_MANAGED, registeredAccount.getCapabilities());
578    }
579
580    private static ComponentName makeQuickConnectionServiceComponentName() {
581        return new ComponentName(
582                "com.android.server.telecom.tests",
583                "com.android.server.telecom.tests.MockConnectionService");
584    }
585
586    private static PhoneAccountHandle makeQuickAccountHandle(String id) {
587        return makeQuickAccountHandle(makeQuickConnectionServiceComponentName(), id);
588    }
589
590    private static PhoneAccountHandle makeQuickAccountHandle(ComponentName name, String id) {
591        return new PhoneAccountHandle(name, id, Process.myUserHandle());
592    }
593
594    private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
595        return new PhoneAccount.Builder(
596                makeQuickAccountHandle(id),
597                "label" + idx);
598    }
599
600    private PhoneAccount makeQuickAccount(String id, int idx) {
601        return makeQuickAccountBuilder(id, idx)
602                .setAddress(Uri.parse("http://foo.com/" + idx))
603                .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
604                .setCapabilities(idx)
605                .setIcon(Icon.createWithResource(
606                            "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
607                .setShortDescription("desc" + idx)
608                .setIsEnabled(true)
609                .build();
610    }
611
612    private static void roundTripPhoneAccount(PhoneAccount original) throws Exception {
613        PhoneAccount copy = null;
614
615        {
616            Parcel parcel = Parcel.obtain();
617            parcel.writeParcelable(original, 0);
618            parcel.setDataPosition(0);
619            copy = parcel.readParcelable(PhoneAccountRegistrarTest.class.getClassLoader());
620            parcel.recycle();
621        }
622
623        assertPhoneAccountEquals(original, copy);
624    }
625
626    private static <T> T roundTripXml(
627            Object self,
628            T input,
629            PhoneAccountRegistrar.XmlSerialization<T> xml,
630            Context context)
631            throws Exception {
632        Log.d(self, "Input = %s", input);
633
634        byte[] data;
635        {
636            XmlSerializer serializer = new FastXmlSerializer();
637            ByteArrayOutputStream baos = new ByteArrayOutputStream();
638            serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
639            xml.writeToXml(input, serializer, context);
640            serializer.flush();
641            data = baos.toByteArray();
642        }
643
644        Log.i(self, "====== XML data ======\n%s", new String(data));
645
646        T result = null;
647        {
648            XmlPullParser parser = Xml.newPullParser();
649            parser.setInput(new BufferedInputStream(new ByteArrayInputStream(data)), null);
650            parser.nextTag();
651            result = xml.readFromXml(parser, MAX_VERSION, context);
652        }
653
654        Log.i(self, "result = " + result);
655
656        return result;
657    }
658
659    private static void assertPhoneAccountHandleEquals(PhoneAccountHandle a, PhoneAccountHandle b) {
660        if (a != b) {
661            assertEquals(
662                    a.getComponentName().getPackageName(),
663                    b.getComponentName().getPackageName());
664            assertEquals(
665                    a.getComponentName().getClassName(),
666                    b.getComponentName().getClassName());
667            assertEquals(a.getId(), b.getId());
668        }
669    }
670
671    private static void assertIconEquals(Icon a, Icon b) {
672        if (a != b) {
673            if (a != null && b != null) {
674                assertEquals(a.toString(), b.toString());
675            } else {
676                fail("Icons not equal: " + a + ", " + b);
677            }
678        }
679    }
680
681    private static void assertDefaultPhoneAccountHandleEquals(DefaultPhoneAccountHandle a,
682            DefaultPhoneAccountHandle b) {
683        if (a != b) {
684            if (a!= null && b != null) {
685                assertEquals(a.userHandle, b.userHandle);
686                assertPhoneAccountHandleEquals(a.phoneAccountHandle, b.phoneAccountHandle);
687            } else {
688                fail("Default phone account handles are not equal: " + a + ", " + b);
689            }
690        }
691    }
692
693    private static void assertPhoneAccountEquals(PhoneAccount a, PhoneAccount b) {
694        if (a != b) {
695            if (a != null && b != null) {
696                assertPhoneAccountHandleEquals(a.getAccountHandle(), b.getAccountHandle());
697                assertEquals(a.getAddress(), b.getAddress());
698                assertEquals(a.getSubscriptionAddress(), b.getSubscriptionAddress());
699                assertEquals(a.getCapabilities(), b.getCapabilities());
700                assertIconEquals(a.getIcon(), b.getIcon());
701                assertEquals(a.getHighlightColor(), b.getHighlightColor());
702                assertEquals(a.getLabel(), b.getLabel());
703                assertEquals(a.getShortDescription(), b.getShortDescription());
704                assertEquals(a.getSupportedUriSchemes(), b.getSupportedUriSchemes());
705                assertBundlesEqual(a.getExtras(), b.getExtras());
706                assertEquals(a.isEnabled(), b.isEnabled());
707            } else {
708                fail("Phone accounts not equal: " + a + ", " + b);
709            }
710        }
711    }
712
713    private static void assertBundlesEqual(Bundle a, Bundle b) {
714        if (a == null && b == null) {
715            return;
716        }
717
718        assertNotNull(a);
719        assertNotNull(b);
720        Set<String> keySetA = a.keySet();
721        Set<String> keySetB = b.keySet();
722
723        assertTrue("Bundle keys not the same", keySetA.containsAll(keySetB));
724        assertTrue("Bundle keys not the same", keySetB.containsAll(keySetA));
725
726        for (String keyA : keySetA) {
727            assertEquals("Bundle value not the same", a.get(keyA), b.get(keyA));
728        }
729    }
730
731    private static void assertStateEquals(
732            PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
733        assertEquals(a.defaultOutgoingAccountHandles.size(),
734                b.defaultOutgoingAccountHandles.size());
735        for (int i = 0; i < a.defaultOutgoingAccountHandles.size(); i++) {
736            assertDefaultPhoneAccountHandleEquals(a.defaultOutgoingAccountHandles.get(i),
737                    b.defaultOutgoingAccountHandles.get(i));
738        }
739        assertEquals(a.accounts.size(), b.accounts.size());
740        for (int i = 0; i < a.accounts.size(); i++) {
741            assertPhoneAccountEquals(a.accounts.get(i), b.accounts.get(i));
742        }
743    }
744
745    private PhoneAccountRegistrar.State makeQuickState() {
746        PhoneAccountRegistrar.State s = new PhoneAccountRegistrar.State();
747        s.accounts.add(makeQuickAccount("id0", 0));
748        s.accounts.add(makeQuickAccount("id1", 1));
749        s.accounts.add(makeQuickAccount("id2", 2));
750        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
751                new ComponentName("pkg0", "cls0"), "id0");
752        UserHandle userHandle = phoneAccountHandle.getUserHandle();
753        s.defaultOutgoingAccountHandles
754                .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle,
755                        "testGroup"));
756        return s;
757    }
758}
759