1 /*
2  * Copyright (C) 2019 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 
17 package com.android.cellbroadcastservice.tests;
18 
19 import static android.telephony.SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE;
20 import static android.telephony.SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE;
21 
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.ArgumentMatchers.nullable;
27 import static org.mockito.Mockito.clearInvocations;
28 import static org.mockito.Mockito.doAnswer;
29 import static org.mockito.Mockito.doReturn;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 
35 import android.app.ActivityManager;
36 import android.app.IActivityManager;
37 import android.content.ContentValues;
38 import android.content.Context;
39 import android.content.IIntentSender;
40 import android.content.Intent;
41 import android.content.res.Configuration;
42 import android.database.Cursor;
43 import android.database.MatrixCursor;
44 import android.net.Uri;
45 import android.os.Bundle;
46 import android.os.IBinder;
47 import android.os.ServiceManager;
48 import android.os.SystemProperties;
49 import android.provider.Telephony;
50 import android.telephony.CbGeoUtils;
51 import android.telephony.SmsCbCmasInfo;
52 import android.telephony.SmsCbLocation;
53 import android.telephony.SmsCbMessage;
54 import android.telephony.SubscriptionInfo;
55 import android.telephony.SubscriptionManager;
56 import android.telephony.TelephonyManager;
57 import android.test.mock.MockContentProvider;
58 import android.test.mock.MockContentResolver;
59 import android.testing.AndroidTestingRunner;
60 import android.testing.TestableLooper;
61 import android.text.format.DateUtils;
62 import android.util.Singleton;
63 
64 import androidx.annotation.NonNull;
65 import androidx.test.filters.SmallTest;
66 
67 import com.android.cellbroadcastservice.CbSendMessageCalculator;
68 import com.android.cellbroadcastservice.CellBroadcastHandler;
69 import com.android.cellbroadcastservice.CellBroadcastProvider;
70 import com.android.cellbroadcastservice.SmsCbConstants;
71 import com.android.internal.telephony.ISub;
72 import com.android.modules.utils.build.SdkLevel;
73 
74 import org.junit.After;
75 import org.junit.Before;
76 import org.junit.Test;
77 import org.junit.runner.RunWith;
78 import org.mockito.Mock;
79 
80 import java.io.IOException;
81 import java.io.OutputStream;
82 import java.io.PrintWriter;
83 import java.util.HashMap;
84 import java.util.List;
85 
86 @RunWith(AndroidTestingRunner.class)
87 @TestableLooper.RunWithLooper
88 public class CellBroadcastHandlerTest extends CellBroadcastServiceTestBase {
89 
90     private CellBroadcastHandler mCellBroadcastHandler;
91 
92     private TestableLooper mTestbleLooper;
93 
94     private CbSendMessageCalculatorFactoryFacade mSendMessageFactory;
95 
96     private CellBroadcastHandler.HandlerHelper mHandlerHelper;
97 
98     protected HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
99 
100     @Mock
101     private IBinder mIBinder;
102 
103     @Mock
104     private IActivityManager mIActivityManager;
105 
106     @Mock
107     private IIntentSender mIIntentSender;
108 
109     @Mock
110     private Singleton<IActivityManager> mIActivityManagerSingleton;
111 
112     @Mock
113     private ISub mISub;
114 
115     private Configuration mConfiguration;
116 
117     private class CellBroadcastContentProvider extends MockContentProvider {
118         String mPlmn = "311480";
119         int mGeographicalScope = 0;
120 
setPlmn(String plmn)121         public void setPlmn(String plmn) {
122             mPlmn = plmn;
123         }
124 
setGeographicalScope(int geographicalScope)125         public void setGeographicalScope(int geographicalScope) {
126             mGeographicalScope = geographicalScope;
127         }
128 
129         @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)130         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
131                 String sortOrder) {
132 
133             if (uri.compareTo(Telephony.CellBroadcasts.CONTENT_URI) == 0) {
134                 MatrixCursor mc = new MatrixCursor(CellBroadcastProvider.QUERY_COLUMNS);
135 
136                 mc.addRow(new Object[]{
137                         1,              // _ID
138                         0,              // SLOT_INDEX
139                         1,              // SUBSCRIPTION_ID
140                         mGeographicalScope, // GEOGRAPHICAL_SCOPE
141                         mPlmn,          // PLMN
142                         0,              // LAC
143                         0,              // CID
144                         1234,           // SERIAL_NUMBER
145                         SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL,
146                         "en",           // LANGUAGE_CODE
147                         1,              // DATA_CODING_SCHEME
148                         "Test Message", // MESSAGE_BODY
149                         1,              // MESSAGE_FORMAT
150                         3,              // MESSAGE_PRIORITY
151                         0,              // ETWS_WARNING_TYPE
152                         0,              // ETWS_IS_PRIMARY
153                         SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, // CMAS_MESSAGE_CLASS
154                         0,              // CMAS_CATEGORY
155                         0,              // CMAS_RESPONSE_TYPE
156                         0,              // CMAS_SEVERITY
157                         0,              // CMAS_URGENCY
158                         0,              // CMAS_CERTAINTY
159                         System.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS * 2,
160                         System.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS * 2,
161                         true,           // MESSAGE_BROADCASTED
162                         true,           // MESSAGE_DISPLAYED
163                         "",             // GEOMETRIES
164                         5,              // MAXIMUM_WAIT_TIME
165                 });
166                 return mc;
167             }
168 
169             return null;
170         }
171 
172         @Override
update(Uri url, ContentValues values, String where, String[] whereArgs)173         public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
174             return 1;
175         }
176     }
177 
178 
179     @Before
setUp()180     public void setUp() throws Exception {
181         super.setUp();
182 
183         mTestbleLooper = TestableLooper.get(CellBroadcastHandlerTest.this);
184         mSendMessageFactory = new CbSendMessageCalculatorFactoryFacade();
185         mHandlerHelper = mock(CellBroadcastHandler.HandlerHelper.class);
186 
187         mCellBroadcastHandler = new CellBroadcastHandler("CellBroadcastHandlerUT",
188                 mMockedContext, mTestbleLooper.getLooper(), mSendMessageFactory, mHandlerHelper);
189 
190         doAnswer(invocation -> {
191             Runnable r = invocation.getArgument(0);
192             mCellBroadcastHandler.getHandler().post(r);
193             return null;
194         }).when(mHandlerHelper).post(any());
195         doReturn(mCellBroadcastHandler.getHandler()).when(mHandlerHelper).getHandler();
196 
197         ((MockContentResolver) mMockedContext.getContentResolver()).addProvider(
198                 Telephony.CellBroadcasts.CONTENT_URI.getAuthority(),
199                 new CellBroadcastContentProvider());
200         doReturn("com.android.cellbroadcastservice").when(mMockedContext).getPackageName();
201         doReturn(mMockedContext).when(mMockedContext).createConfigurationContext(any());
202         doReturn(mMockedResources).when(mMockedContext).getResources();
203         mConfiguration = new Configuration();
204         doReturn(mConfiguration).when(mMockedResources).getConfiguration();
205         putResources(com.android.cellbroadcastservice.R.integer.message_expiration_time,
206                 (int) DateUtils.DAY_IN_MILLIS);
207         putResources(com.android.cellbroadcastservice.R.bool.duplicate_compare_service_category,
208                 true);
209 
210         replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
211                 mIActivityManagerSingleton);
212 
213         replaceInstance(Singleton.class, "mInstance", mIActivityManagerSingleton,
214                 mIActivityManager);
215         replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
216 
217         doReturn(mIIntentSender).when(mIActivityManager).getIntentSenderWithFeature(anyInt(),
218                 nullable(String.class), nullable(String.class), nullable(IBinder.class),
219                 nullable(String.class), anyInt(), nullable(Intent[].class),
220                 nullable(String[].class), anyInt(), nullable(Bundle.class), anyInt());
221         doReturn(mIBinder).when(mIIntentSender).asBinder();
222         doReturn(mISub).when(mIBinder).queryLocalInterface(anyString());
223         mServiceManagerMockedServices.put("isub", mIBinder);
224         TelephonyManager.disableServiceHandleCaching();
225         SubscriptionManager.disableCaching();
226     }
227 
228     @After
tearDown()229     public void tearDown() throws Exception {
230         super.tearDown();
231     }
232 
createSmsCbMessage(int serialNumber, int serviceCategory, String messageBody)233     private SmsCbMessage createSmsCbMessage(int serialNumber, int serviceCategory,
234             String messageBody) {
235         return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
236                 0, serialNumber, new SmsCbLocation("311480", 0, 0),
237                 serviceCategory, "en", messageBody, 3,
238                 null, null, 0, 1);
239     }
240 
241     @Test
242     @SmallTest
testDuplicate()243     public void testDuplicate() throws Exception {
244         SmsCbMessage msg = createSmsCbMessage(1234, 4370, "msg");
245         assertTrue(mCellBroadcastHandler.isDuplicate(msg));
246     }
247 
248     @Test
249     @SmallTest
testNotDuplicateSerialDifferent()250     public void testNotDuplicateSerialDifferent() throws Exception {
251         SmsCbMessage msg = createSmsCbMessage(1235, 4370, "msg");
252         assertFalse(mCellBroadcastHandler.isDuplicate(msg));
253     }
254 
255     @Test
256     @SmallTest
testNotDuplicateServiceCategoryDifferent()257     public void testNotDuplicateServiceCategoryDifferent() throws Exception {
258         SmsCbMessage msg = createSmsCbMessage(1234, 4371, "msg");
259         assertFalse(mCellBroadcastHandler.isDuplicate(msg));
260     }
261 
262     @Test
263     @SmallTest
testNotDuplicateMessageBodyDifferent()264     public void testNotDuplicateMessageBodyDifferent() throws Exception {
265         putResources(com.android.cellbroadcastservice.R.bool.duplicate_compare_body, true);
266         SmsCbMessage msg = createSmsCbMessage(1234, 4370, "msg");
267         assertFalse(mCellBroadcastHandler.isDuplicate(msg));
268     }
269 
270     @Test
271     @SmallTest
testNotDuplicateCellLocationDifferent()272     public void testNotDuplicateCellLocationDifferent() throws Exception {
273         SmsCbMessage msg = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
274                 0, 1234, new SmsCbLocation("311480", 0, 1),
275                 4370, "en", "Test Message", 3,
276                 null, null, 0, 1);
277         assertFalse(mCellBroadcastHandler.isDuplicate(msg));
278     }
279 
280     @Test
281     @SmallTest
testMakeCellBroadcastHandler()282     public void testMakeCellBroadcastHandler() throws Exception {
283         CellBroadcastHandler cellBroadcastHandler =
284                 CellBroadcastHandler.makeCellBroadcastHandler(mMockedContext);
285         // sanity test for make, just assert that returned object is not null
286         assertTrue(cellBroadcastHandler != null);
287         cellBroadcastHandler.cleanup();
288     }
289 
290     @Test
291     @SmallTest
testDump()292     public void testDump() throws Exception {
293             mCellBroadcastHandler.dump(null, new PrintWriter(new OutputStream() {
294                 @Override
295                 public void write(int b) throws IOException {
296                     // no implementation needed for sanity test
297                 }
298             }), null);
299     }
300 
301     @Test
302     @SmallTest
testPutPhoneIdAndSubIdExtra()303     public void testPutPhoneIdAndSubIdExtra() throws Exception {
304         Intent intent = new Intent();
305         int phoneId = 0;
306         if (SdkLevel.isAtLeastU()) {
307             doReturn(FAKE_SUBID).when(mISub).getSubId(phoneId);
308         }
309         CellBroadcastHandler.putPhoneIdAndSubIdExtra(mMockedContext, intent, phoneId);
310         assertEquals(FAKE_SUBID, intent.getIntExtra(
311                 SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, FAKE_SUBID + 1));
312         assertEquals(FAKE_SUBID, intent.getIntExtra(
313                 "subscription", FAKE_SUBID + 1));
314         assertEquals(phoneId, intent.getIntExtra(
315                 SubscriptionManager.EXTRA_SLOT_INDEX, phoneId + 1));
316         assertEquals(phoneId, intent.getIntExtra("phone", phoneId + 1));
317 
318         // if subId is not available, subscription extras should not be added
319         Intent intentNoSubId = new Intent();
320         if (SdkLevel.isAtLeastU()) {
321             doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mISub).getSubId(phoneId);
322         } else {
323             doReturn(null).when(mMockedSubscriptionManager).getSubscriptionIds(anyInt());
324         }
325         CellBroadcastHandler.putPhoneIdAndSubIdExtra(mMockedContext, intentNoSubId, phoneId);
326         assertEquals(FAKE_SUBID + 1, intentNoSubId.getIntExtra(
327                 SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, FAKE_SUBID + 1));
328         assertEquals(FAKE_SUBID + 1, intentNoSubId.getIntExtra(
329                 "subscription", FAKE_SUBID + 1));
330         assertEquals(phoneId, intentNoSubId.getIntExtra(
331                 SubscriptionManager.EXTRA_SLOT_INDEX, phoneId + 1));
332         assertEquals(phoneId, intentNoSubId.getIntExtra(
333                 "phone", phoneId + 1));
334     }
335 
336     @Test
337     @SmallTest
testDuplicateDetection()338     public void testDuplicateDetection() throws Exception {
339         // if IS_DEBUGGABLE
340         if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
341             SmsCbMessage msg = createSmsCbMessage(1234, 4370, "msg");
342             // message should be detected as duplicate initially
343             assertTrue(mCellBroadcastHandler.isDuplicate(msg));
344 
345             // disable duplicate detection
346             Intent intent =
347                     new Intent("com.android.cellbroadcastservice.action.DUPLICATE_DETECTION");
348             intent.putExtra("enable", false);
349             sendBroadcast(intent);
350 
351             // message should not be detected as duplicate
352             assertFalse(mCellBroadcastHandler.isDuplicate(msg));
353 
354             // enable duplicate detection
355             intent.putExtra("enable", true);
356             sendBroadcast(intent);
357 
358             // message should be detected as duplicate again
359             assertTrue(mCellBroadcastHandler.isDuplicate(msg));
360         }
361     }
362 
363     @Test
364     @SmallTest
testEmptyPlmnDuplicateDetection()365     public void testEmptyPlmnDuplicateDetection() throws Exception {
366 
367         // case (GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE + plmn exist)
368         SmsCbMessage msg1 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
369                 GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE, 1234, new SmsCbLocation("311480", 0, 0),
370                 4370, "en", "Test Message1", 3,
371                 null, null, 0, 1);
372         assertTrue(mCellBroadcastHandler.isDuplicate(msg1));
373 
374         // case (GEOGRAPHICAL_SCOPE_PLMN_WIDE + plmn exist)
375         SmsCbMessage msg2 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
376                 GEOGRAPHICAL_SCOPE_PLMN_WIDE, 1234, new SmsCbLocation("311480", 0, 0),
377                 4370, "en", "Test Message2", 3,
378                 null, null, 0, 1);
379         assertFalse(mCellBroadcastHandler.isDuplicate(msg2));
380 
381         // case (GEOGRAPHICAL_SCOPE_PLMN_WIDE + plmn "")
382         SmsCbMessage msg3 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
383                 GEOGRAPHICAL_SCOPE_PLMN_WIDE, 1234, new SmsCbLocation("", 0, 0),
384                 4370, "en", "Test Message2", 3,
385                 null, null, 0, 1);
386         assertFalse(mCellBroadcastHandler.isDuplicate(msg3));
387 
388         CellBroadcastContentProvider CBContentProviderForEmptyPlmn =
389                 new CellBroadcastContentProvider();
390         CBContentProviderForEmptyPlmn.setPlmn("");
391         CBContentProviderForEmptyPlmn.setGeographicalScope(GEOGRAPHICAL_SCOPE_PLMN_WIDE);
392         mMockedContentResolver.addProvider(Telephony.CellBroadcasts.CONTENT_URI.getAuthority(),
393                 CBContentProviderForEmptyPlmn);
394 
395         // case (GEOGRAPHICAL_SCOPE_PLMN_WIDE + plmn "") with already empty plmn message saved
396         SmsCbMessage msg4 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
397                 GEOGRAPHICAL_SCOPE_PLMN_WIDE, 1234, new SmsCbLocation("", 0, 0),
398                 4370, "en", "Test Message3", 3,
399                 null, null, 0, 1);
400         assertTrue(mCellBroadcastHandler.isDuplicate(msg4));
401     }
402 
403     @Test
404     @SmallTest
testCrossSimDuplicateDetection()405     public void testCrossSimDuplicateDetection() throws Exception {
406         int differentSlotID = 1;
407         int differentSubID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
408 
409         // enable cross_sim_duplicate_detection
410         putResources(com.android.cellbroadcastservice.R.bool.cross_sim_duplicate_detection, true);
411 
412         // The message with different subId will be detected as duplication.
413         SmsCbMessage msg1 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
414                 0, 1234, new SmsCbLocation("311480", 0, 0),
415                 4370, "en", "Test Message", 3,
416                 null, null, 0, differentSubID);
417         assertTrue(mCellBroadcastHandler.isDuplicate(msg1));
418 
419         // The message with different body won't be detected as a duplication.
420         SmsCbMessage msg2 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
421                 0, 1234, new SmsCbLocation("311480", 0, 0),
422                 4370, "en", "Different Message", 3,
423                 null, null, 0, differentSubID);
424         assertFalse(mCellBroadcastHandler.isDuplicate(msg2));
425 
426         // The message with different slotId will be detected as a duplication.
427         SmsCbMessage msg3 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
428                 0, 1234, new SmsCbLocation("311480", 0, 0),
429                 4370, "en", "Test Message", 3,
430                 null, null, differentSlotID, 1);
431         assertTrue(mCellBroadcastHandler.isDuplicate(msg3));
432 
433         // The message with different slotId and body will be detected as a duplication.
434         SmsCbMessage msg4 = new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
435                 0, 1234, new SmsCbLocation("311480", 0, 0),
436                 4370, "en", "Different Message", 3,
437                 null, null, differentSlotID, 1);
438         assertTrue(mCellBroadcastHandler.isDuplicate(msg4));
439     }
440 
441     @Test
442     @SmallTest
testGetResources()443     public void testGetResources() throws Exception {
444         SubscriptionInfo mockSubInfo = mock(SubscriptionInfo.class);
445         doReturn(mockSubInfo).when(mMockedSubscriptionManager).getActiveSubscriptionInfo(anyInt());
446         doReturn(001).when(mockSubInfo).getMcc();
447         doReturn(01).when(mockSubInfo).getMnc();
448 
449         // verify not to call SubscriptionManager#getResourcesForSubId for DEFAULT ID
450         mCellBroadcastHandler.getResources(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
451 
452         verify(mMockedContext, never()).createConfigurationContext(any());
453         verify(mMockedContext, times(1)).getResources();
454 
455         // verify not to call SubscriptionManager#getResourcesForSubId for INVALID ID
456         mCellBroadcastHandler.getResources(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
457 
458         verify(mMockedContext, never()).createConfigurationContext(any());
459         verify(mMockedContext, times(2)).getResources();
460 
461         // verify to call SubscriptionManager#getResourcesForSubId for normal sub id
462         mCellBroadcastHandler.getResources(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 1);
463 
464         verify(mMockedContext, times(1)).createConfigurationContext(any());
465 
466         // verify to call SubscriptionManager#getResourcesForSubId again for the same sub
467         mCellBroadcastHandler.getResources(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 1);
468 
469         verify(mMockedContext, times(1)).createConfigurationContext(any());
470     }
471 
472     @Test
473     @SmallTest
testConstructorRegistersReceiverWithExpectedFlag()474     public void testConstructorRegistersReceiverWithExpectedFlag() {
475         int expectedFlag = SdkLevel.isAtLeastT() ? Context.RECEIVER_EXPORTED : 0;
476         clearInvocations(mMockedContext);
477 
478         CellBroadcastHandler cellBroadcastHandler = new CellBroadcastHandler(
479                 "CellBroadcastHandlerUT", mMockedContext, mTestbleLooper.getLooper(),
480                 mSendMessageFactory, mHandlerHelper);
481 
482         verify(mMockedContext, times(1)).registerReceiver(any(), any(), eq(expectedFlag));
483         cellBroadcastHandler.cleanup();
484     }
485 
486     /**
487      * Makes injecting a mock factory easy.
488      */
489     static class CbSendMessageCalculatorFactoryFacade extends
490             CellBroadcastHandler.CbSendMessageCalculatorFactory {
491 
492         @NonNull
493         private CellBroadcastHandler.CbSendMessageCalculatorFactory mUnderlyingFactory;
494 
getUnderlyingFactory()495         @NonNull CellBroadcastHandler.CbSendMessageCalculatorFactory getUnderlyingFactory() {
496             return mUnderlyingFactory;
497         }
498 
setUnderlyingFactory( @onNull final CellBroadcastHandler.CbSendMessageCalculatorFactory factory)499         void setUnderlyingFactory(
500                 @NonNull final CellBroadcastHandler.CbSendMessageCalculatorFactory factory) {
501             mUnderlyingFactory = factory;
502         }
503 
CbSendMessageCalculatorFactoryFacade()504         CbSendMessageCalculatorFactoryFacade() {
505             mUnderlyingFactory = new CellBroadcastHandler.CbSendMessageCalculatorFactory();
506         }
507 
508         @Override
createNew(@onNull Context context, @NonNull List<CbGeoUtils.Geometry> fences)509         public CbSendMessageCalculator createNew(@NonNull Context context,
510                 @NonNull List<CbGeoUtils.Geometry> fences) {
511             return mUnderlyingFactory.createNew(context, fences);
512         }
513     }
514 }
515