PhoneAccountRegistrarTest.java revision a3799ae6aafba8ccd6448a7c4337acd3460b49f2
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.PhoneAccount;
30import android.telecom.PhoneAccountHandle;
31import android.telecom.TelecomManager;
32import android.test.suitebuilder.annotation.MediumTest;
33import android.util.Xml;
34
35import com.android.internal.telecom.IConnectionService;
36import com.android.internal.util.FastXmlSerializer;
37import com.android.server.telecom.Log;
38import com.android.server.telecom.PhoneAccountRegistrar;
39import com.android.server.telecom.PhoneAccountRegistrar.DefaultPhoneAccountHandle;
40
41import org.mockito.Mock;
42import org.mockito.Mockito;
43import org.mockito.MockitoAnnotations;
44import org.xmlpull.v1.XmlPullParser;
45import org.xmlpull.v1.XmlSerializer;
46
47import java.io.BufferedInputStream;
48import java.io.BufferedOutputStream;
49import java.io.ByteArrayInputStream;
50import java.io.ByteArrayOutputStream;
51import java.io.File;
52import java.util.Arrays;
53import java.util.Set;
54
55public class PhoneAccountRegistrarTest extends TelecomTestCase {
56
57    private static final int MAX_VERSION = Integer.MAX_VALUE;
58    private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
59    private PhoneAccountRegistrar mRegistrar;
60    @Mock
61    private TelecomManager mTelecomManager;
62
63    @Override
64    public void setUp() throws Exception {
65        super.setUp();
66        MockitoAnnotations.initMocks(this);
67        mComponentContextFixture.setTelecomManager(mTelecomManager);
68        new File(
69                mComponentContextFixture.getTestDouble().getApplicationContext().getFilesDir(),
70                FILE_NAME)
71                .delete();
72        mRegistrar = new PhoneAccountRegistrar(
73                mComponentContextFixture.getTestDouble().getApplicationContext(),
74                FILE_NAME);
75    }
76
77    @Override
78    public void tearDown() throws Exception {
79        mRegistrar = null;
80        new File(
81                mComponentContextFixture.getTestDouble().getApplicationContext().getFilesDir(),
82                FILE_NAME)
83                .delete();
84        super.tearDown();
85    }
86
87    @MediumTest
88    public void testPhoneAccountHandle() throws Exception {
89        PhoneAccountHandle input = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), "id0");
90        PhoneAccountHandle result = roundTripXml(this, input,
91                PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
92        assertPhoneAccountHandleEquals(input, result);
93
94        PhoneAccountHandle inputN = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), null);
95        PhoneAccountHandle resultN = roundTripXml(this, inputN,
96                PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
97        Log.i(this, "inputN = %s, resultN = %s", inputN, resultN);
98        assertPhoneAccountHandleEquals(inputN, resultN);
99    }
100
101    @MediumTest
102    public void testPhoneAccount() throws Exception {
103        Bundle testBundle = new Bundle();
104        testBundle.putInt("EXTRA_INT_1", 1);
105        testBundle.putInt("EXTRA_INT_100", 100);
106        testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
107        testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
108        testBundle.putString("EXTRA_STR1", "Hello");
109        testBundle.putString("EXTRA_STR2", "There");
110
111        PhoneAccount input = makeQuickAccountBuilder("id0", 0)
112                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
113                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
114                .setExtras(testBundle)
115                .setIsEnabled(true)
116                .build();
117        PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
118                mContext);
119
120        assertPhoneAccountEquals(input, result);
121    }
122
123    /**
124     * Test to ensure non-supported values
125     * @throws Exception
126     */
127    @MediumTest
128    public void testPhoneAccountExtrasEdge() throws Exception {
129        Bundle testBundle = new Bundle();
130        // Ensure null values for string are not persisted.
131        testBundle.putString("EXTRA_STR2", null);
132        //
133
134        // Ensure unsupported data types are not persisted.
135        testBundle.putShort("EXTRA_SHORT", (short) 2);
136        testBundle.putByte("EXTRA_BYTE", (byte) 1);
137        testBundle.putParcelable("EXTRA_PARC", new Rect(1, 1, 1, 1));
138        // Put in something valid so the bundle exists.
139        testBundle.putString("EXTRA_OK", "OK");
140
141        PhoneAccount input = makeQuickAccountBuilder("id0", 0)
142                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
143                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
144                .setExtras(testBundle)
145                .build();
146        PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
147                mContext);
148
149        Bundle extras = result.getExtras();
150        assertFalse(extras.keySet().contains("EXTRA_STR2"));
151        assertFalse(extras.keySet().contains("EXTRA_SHORT"));
152        assertFalse(extras.keySet().contains("EXTRA_BYTE"));
153        assertFalse(extras.keySet().contains("EXTRA_PARC"));
154    }
155
156    @MediumTest
157    public void testState() throws Exception {
158        PhoneAccountRegistrar.State input = makeQuickState();
159        PhoneAccountRegistrar.State result = roundTripXml(this, input,
160                PhoneAccountRegistrar.sStateXml,
161                mContext);
162        assertStateEquals(input, result);
163    }
164
165    private void registerAndEnableAccount(PhoneAccount account) {
166        mRegistrar.registerPhoneAccount(account);
167        mRegistrar.enablePhoneAccount(account.getAccountHandle(), true);
168    }
169
170    @MediumTest
171    public void testAccounts() throws Exception {
172        int i = 0;
173
174        mComponentContextFixture.addConnectionService(
175                makeQuickConnectionServiceComponentName(),
176                Mockito.mock(IConnectionService.class));
177
178        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
179                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
180                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
181                .build());
182        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
183                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
184                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
185                .build());
186        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
187                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
188                        | PhoneAccount.CAPABILITY_CALL_PROVIDER)
189                .build());
190        registerAndEnableAccount(makeQuickAccountBuilder("id" + i, i++)
191                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
192                .build());
193
194        assertEquals(4, mRegistrar.getAllPhoneAccountsOfCurrentUser().size());
195        assertEquals(3, mRegistrar.getCallCapablePhoneAccountsOfCurrentUser(null, false).size());
196        assertEquals(null, mRegistrar.getSimCallManagerOfCurrentUser());
197        assertEquals(null, mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
198                PhoneAccount.SCHEME_TEL));
199    }
200
201    @MediumTest
202    public void testSimCallManager() throws Exception {
203        // TODO
204    }
205
206    @MediumTest
207    public void testDefaultOutgoing() throws Exception {
208        mComponentContextFixture.addConnectionService(
209                makeQuickConnectionServiceComponentName(),
210                Mockito.mock(IConnectionService.class));
211
212        // By default, there is no default outgoing account (nothing has been registered)
213        assertNull(
214                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
215
216        // Register one tel: account
217        PhoneAccountHandle telAccount = makeQuickAccountHandle("tel_acct");
218        registerAndEnableAccount(new PhoneAccount.Builder(telAccount, "tel_acct")
219                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
220                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
221                .build());
222        PhoneAccountHandle defaultAccount =
223                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
224        assertEquals(telAccount, defaultAccount);
225
226        // Add a SIP account, make sure tel: doesn't change
227        PhoneAccountHandle sipAccount = makeQuickAccountHandle("sip_acct");
228        registerAndEnableAccount(new PhoneAccount.Builder(sipAccount, "sip_acct")
229                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
230                .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
231                .build());
232        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
233                PhoneAccount.SCHEME_SIP);
234        assertEquals(sipAccount, defaultAccount);
235        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
236                PhoneAccount.SCHEME_TEL);
237        assertEquals(telAccount, defaultAccount);
238
239        // Add a connection manager, make sure tel: doesn't change
240        PhoneAccountHandle connectionManager = makeQuickAccountHandle("mgr_acct");
241        registerAndEnableAccount(new PhoneAccount.Builder(connectionManager, "mgr_acct")
242                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
243                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
244                .build());
245        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
246                PhoneAccount.SCHEME_TEL);
247        assertEquals(telAccount, defaultAccount);
248
249        // Unregister the tel: account, make sure there is no tel: default now.
250        mRegistrar.unregisterPhoneAccount(telAccount);
251        assertNull(
252                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
253    }
254
255    @MediumTest
256    public void testPhoneAccountParceling() throws Exception {
257        PhoneAccountHandle handle = makeQuickAccountHandle("foo");
258        roundTripPhoneAccount(new PhoneAccount.Builder(handle, null).build());
259        roundTripPhoneAccount(new PhoneAccount.Builder(handle, "foo").build());
260        roundTripPhoneAccount(
261                new PhoneAccount.Builder(handle, "foo")
262                        .setAddress(Uri.parse("tel:123456"))
263                        .setCapabilities(23)
264                        .setHighlightColor(0xf0f0f0)
265                        .setIcon(Icon.createWithResource(
266                                "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
267                        // TODO: set icon tint (0xfefefe)
268                        .setShortDescription("short description")
269                        .setSubscriptionAddress(Uri.parse("tel:2345678"))
270                        .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
271                        .build());
272        roundTripPhoneAccount(
273                new PhoneAccount.Builder(handle, "foo")
274                        .setAddress(Uri.parse("tel:123456"))
275                        .setCapabilities(23)
276                        .setHighlightColor(0xf0f0f0)
277                        .setIcon(Icon.createWithBitmap(
278                                BitmapFactory.decodeResource(
279                                        getContext().getResources(),
280                                        R.drawable.stat_sys_phone_call)))
281                        .setShortDescription("short description")
282                        .setSubscriptionAddress(Uri.parse("tel:2345678"))
283                        .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
284                        .build());
285    }
286
287    private static ComponentName makeQuickConnectionServiceComponentName() {
288        return new ComponentName(
289                "com.android.server.telecom.tests",
290                "com.android.server.telecom.tests.MockConnectionService");
291    }
292
293    private static PhoneAccountHandle makeQuickAccountHandle(String id) {
294        return new PhoneAccountHandle(
295                makeQuickConnectionServiceComponentName(),
296                id,
297                Process.myUserHandle());
298    }
299
300    private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
301        return new PhoneAccount.Builder(
302                makeQuickAccountHandle(id),
303                "label" + idx);
304    }
305
306    private PhoneAccount makeQuickAccount(String id, int idx) {
307        return makeQuickAccountBuilder(id, idx)
308                .setAddress(Uri.parse("http://foo.com/" + idx))
309                .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
310                .setCapabilities(idx)
311                .setIcon(Icon.createWithResource(
312                            "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
313                .setShortDescription("desc" + idx)
314                .setIsEnabled(true)
315                .build();
316    }
317
318    private static void roundTripPhoneAccount(PhoneAccount original) throws Exception {
319        PhoneAccount copy = null;
320
321        {
322            Parcel parcel = Parcel.obtain();
323            parcel.writeParcelable(original, 0);
324            parcel.setDataPosition(0);
325            copy = parcel.readParcelable(PhoneAccountRegistrarTest.class.getClassLoader());
326            parcel.recycle();
327        }
328
329        assertPhoneAccountEquals(original, copy);
330    }
331
332    private static <T> T roundTripXml(
333            Object self,
334            T input,
335            PhoneAccountRegistrar.XmlSerialization<T> xml,
336            Context context)
337            throws Exception {
338        Log.d(self, "Input = %s", input);
339
340        byte[] data;
341        {
342            XmlSerializer serializer = new FastXmlSerializer();
343            ByteArrayOutputStream baos = new ByteArrayOutputStream();
344            serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
345            xml.writeToXml(input, serializer, context);
346            serializer.flush();
347            data = baos.toByteArray();
348        }
349
350        Log.i(self, "====== XML data ======\n%s", new String(data));
351
352        T result = null;
353        {
354            XmlPullParser parser = Xml.newPullParser();
355            parser.setInput(new BufferedInputStream(new ByteArrayInputStream(data)), null);
356            parser.nextTag();
357            result = xml.readFromXml(parser, MAX_VERSION, context);
358        }
359
360        Log.i(self, "result = " + result);
361
362        return result;
363    }
364
365    private static void assertPhoneAccountHandleEquals(PhoneAccountHandle a, PhoneAccountHandle b) {
366        if (a != b) {
367            assertEquals(
368                    a.getComponentName().getPackageName(),
369                    b.getComponentName().getPackageName());
370            assertEquals(
371                    a.getComponentName().getClassName(),
372                    b.getComponentName().getClassName());
373            assertEquals(a.getId(), b.getId());
374        }
375    }
376
377    private static void assertIconEquals(Icon a, Icon b) {
378        if (a != b) {
379            if (a != null && b != null) {
380                assertEquals(a.toString(), b.toString());
381            } else {
382                fail("Icons not equal: " + a + ", " + b);
383            }
384        }
385    }
386
387    private static void assertDefaultPhoneAccountHandleEquals(DefaultPhoneAccountHandle a,
388            DefaultPhoneAccountHandle b) {
389        if (a != b) {
390            if (a!= null && b != null) {
391                assertEquals(a.userHandle, b.userHandle);
392                assertPhoneAccountHandleEquals(a.phoneAccountHandle, b.phoneAccountHandle);
393            } else {
394                fail("Default phone account handles are not equal: " + a + ", " + b);
395            }
396        }
397    }
398
399    private static void assertPhoneAccountEquals(PhoneAccount a, PhoneAccount b) {
400        if (a != b) {
401            if (a != null && b != null) {
402                assertPhoneAccountHandleEquals(a.getAccountHandle(), b.getAccountHandle());
403                assertEquals(a.getAddress(), b.getAddress());
404                assertEquals(a.getSubscriptionAddress(), b.getSubscriptionAddress());
405                assertEquals(a.getCapabilities(), b.getCapabilities());
406                assertIconEquals(a.getIcon(), b.getIcon());
407                assertEquals(a.getHighlightColor(), b.getHighlightColor());
408                assertEquals(a.getLabel(), b.getLabel());
409                assertEquals(a.getShortDescription(), b.getShortDescription());
410                assertEquals(a.getSupportedUriSchemes(), b.getSupportedUriSchemes());
411                assertBundlesEqual(a.getExtras(), b.getExtras());
412                assertEquals(a.isEnabled(), b.isEnabled());
413            } else {
414                fail("Phone accounts not equal: " + a + ", " + b);
415            }
416        }
417    }
418
419    private static void assertBundlesEqual(Bundle a, Bundle b) {
420        if (a == null && b == null) {
421            return;
422        }
423
424        assertNotNull(a);
425        assertNotNull(b);
426        Set<String> keySetA = a.keySet();
427        Set<String> keySetB = b.keySet();
428
429        assertTrue("Bundle keys not the same", keySetA.containsAll(keySetB));
430        assertTrue("Bundle keys not the same", keySetB.containsAll(keySetA));
431
432        for (String keyA : keySetA) {
433            assertEquals("Bundle value not the same", a.get(keyA), b.get(keyA));
434        }
435    }
436
437    private static void assertStateEquals(
438            PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
439        assertEquals(a.defaultOutgoingAccountHandles.size(),
440                b.defaultOutgoingAccountHandles.size());
441        for (int i = 0; i < a.defaultOutgoingAccountHandles.size(); i++) {
442            assertDefaultPhoneAccountHandleEquals(a.defaultOutgoingAccountHandles.get(i),
443                    b.defaultOutgoingAccountHandles.get(i));
444        }
445        assertEquals(a.accounts.size(), b.accounts.size());
446        for (int i = 0; i < a.accounts.size(); i++) {
447            assertPhoneAccountEquals(a.accounts.get(i), b.accounts.get(i));
448        }
449    }
450
451    private PhoneAccountRegistrar.State makeQuickState() {
452        PhoneAccountRegistrar.State s = new PhoneAccountRegistrar.State();
453        s.accounts.add(makeQuickAccount("id0", 0));
454        s.accounts.add(makeQuickAccount("id1", 1));
455        s.accounts.add(makeQuickAccount("id2", 2));
456        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
457                new ComponentName("pkg0", "cls0"), "id0");
458        UserHandle userHandle = phoneAccountHandle.getUserHandle();
459        s.defaultOutgoingAccountHandles
460                .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle));
461        return s;
462    }
463}
464