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