blob: 05581ed69bc9ddd7f22e21e8cbf1016ac4df2d64 [file] [log] [blame]
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +00001/*
2 * Copyright 2017-present Open Networking Foundation
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 */
16package org.opencord.igmpproxy;
17
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080018import com.google.common.collect.ImmutableSet;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000019import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080022import org.onlab.packet.MacAddress;
Shubham Sharma47f2caf2020-02-18 12:13:40 +000023import org.onosproject.cfg.ComponentConfigService;
24import org.onosproject.cfg.ConfigProperty;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000025import org.onosproject.core.ApplicationId;
Shubham Sharma47f2caf2020-02-18 12:13:40 +000026import org.onosproject.event.DefaultEventSinkRegistry;
27import org.onosproject.event.Event;
28import org.onosproject.event.EventDeliveryService;
29import org.onosproject.event.EventSink;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000030import org.onosproject.mastership.MastershipServiceAdapter;
31import org.onosproject.mcast.api.McastListener;
32import org.onosproject.mcast.api.McastRoute;
33import org.onosproject.mcast.api.McastRouteData;
34import org.onosproject.mcast.api.MulticastRouteService;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000035import org.onosproject.net.AnnotationKeys;
36import org.onosproject.net.Annotations;
37import org.onosproject.net.ConnectPoint;
38import org.onosproject.net.DefaultAnnotations;
39import org.onosproject.net.DefaultDevice;
Esin Karaman00e16b72020-02-21 10:32:39 +000040import org.onosproject.net.DefaultPort;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000041import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.HostId;
44import org.onosproject.net.Port;
45import org.onosproject.net.PortNumber;
46import org.onosproject.net.SparseAnnotations;
47import org.onosproject.net.config.Config;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080048import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigRegistryAdapter;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000050import org.onosproject.net.config.basics.McastConfig;
51import org.onosproject.net.config.basics.SubjectFactories;
52import org.onosproject.net.device.DeviceServiceAdapter;
53import org.onosproject.net.packet.DefaultInboundPacket;
54import org.onosproject.net.packet.DefaultPacketContext;
55import org.onosproject.net.packet.InboundPacket;
56import org.onosproject.net.packet.OutboundPacket;
57import org.onosproject.net.packet.PacketContext;
58import org.onosproject.net.packet.PacketProcessor;
59import org.onosproject.net.packet.PacketServiceAdapter;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080060import org.opencord.sadis.BandwidthProfileInformation;
61import org.opencord.sadis.BaseInformationService;
62import org.opencord.sadis.SadisService;
63import org.opencord.sadis.SubscriberAndDeviceInformation;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000064import org.slf4j.Logger;
65import org.slf4j.LoggerFactory;
Shubham Sharma47f2caf2020-02-18 12:13:40 +000066import org.osgi.framework.Bundle;
67import org.osgi.framework.BundleContext;
68import org.osgi.framework.ServiceReference;
69import org.osgi.service.component.ComponentContext;
70import org.osgi.service.component.ComponentInstance;
71
72import static com.google.common.base.Preconditions.checkState;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000073
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080074import java.nio.ByteBuffer;
75import java.util.ArrayList;
Shubham Sharma47f2caf2020-02-18 12:13:40 +000076import java.util.Dictionary;
77import java.util.Hashtable;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080078import java.util.LinkedList;
79import java.util.List;
80import java.util.Set;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +000081
82public class IgmpManagerBase {
83
84 // Device configuration
85 protected static final DeviceId DEVICE_ID_OF_A = DeviceId.deviceId("of:1");
86 protected static final DeviceId DEVICE_ID_OF_B = DeviceId.deviceId("of:2");
87
88 //Multicast ip address
89 protected static final Ip4Address GROUP_IP = Ip4Address.valueOf("224.0.0.0");
90 // Source ip address of two different device.
91 protected static final Ip4Address SOURCE_IP_OF_A = Ip4Address.valueOf("10.177.125.4");
92 protected static final Ip4Address SOURCE_IP_OF_B = Ip4Address.valueOf("10.177.125.5");
93
94 // Common connect point of aggregation switch used by all devices.
95 protected static final ConnectPoint COMMON_CONNECT_POINT =
96 ConnectPoint.deviceConnectPoint("of:00000000000000003/3");
97 // Uplink ports for two olts A and B
98 protected static final PortNumber PORT_A = PortNumber.portNumber(1);
99 protected static final PortNumber PORT_B = PortNumber.portNumber(2);
Esin Karaman00e16b72020-02-21 10:32:39 +0000100 protected static final PortNumber PORT_NNI = PortNumber.portNumber(65536);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000101
102 // Connect Point mode for two olts
103 protected static final ConnectPoint CONNECT_POINT_A = new ConnectPoint(DEVICE_ID_OF_A, PORT_A);
104 protected static final ConnectPoint CONNECT_POINT_B = new ConnectPoint(DEVICE_ID_OF_B, PORT_B);
105
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800106 protected static final String CLIENT_NAS_PORT_ID = "PON 1/1";
107 protected static final String CLIENT_CIRCUIT_ID = "CIR-PON 1/1";
108 protected String dsBpId = "HSIA-DS";
109
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000110 private static final int STATISTICS_GEN_PERIOD_IN_SEC = 2;
111
Esin Karaman00e16b72020-02-21 10:32:39 +0000112 private static final String NNI_PREFIX = "nni";
113
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000114 protected List<Port> lsPorts = new ArrayList<Port>();
115 // Flag for adding two different devices in oltData
116 protected boolean flagForDevice = true;
117 PacketContext context;
118 // Flag for sending two different packets
119 protected boolean flagForPacket = true;
120 // List to store the packets emitted
121 protected List<OutboundPacket> savedPackets;
122 protected PacketProcessor packetProcessor;
123 private Logger log = LoggerFactory.getLogger(getClass());
124
125 class MockDeviceService extends DeviceServiceAdapter {
126
127 @Override
128 public Device getDevice(DeviceId deviceId) {
129 if (flagForDevice) {
130 DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
131 .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_A.toString());
132 SparseAnnotations annotations = annotationsBuilder.build();
133 Annotations[] da = {annotations };
134 Device deviceA = new DefaultDevice(null, DEVICE_ID_OF_A, Device.Type.OTHER, "", "", "", "", null, da);
135 flagForDevice = false;
136 return deviceA;
137 } else {
138 DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
139 .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_B.toString());
140 SparseAnnotations annotations = annotationsBuilder.build();
141 Annotations[] da = {annotations };
142 Device deviceB = new DefaultDevice(null, DEVICE_ID_OF_B, Device.Type.OTHER, "", "", "", "", null, da);
143 return deviceB;
144 }
145 }
146 @Override
147 public List<Port> getPorts(DeviceId deviceId) {
148 return lsPorts;
149 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000150
151 @Override
152 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
153 if (portNumber.equals(PORT_NNI)) {
154 DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
155 .set(AnnotationKeys.PORT_NAME, NNI_PREFIX);
156 Port nni = new DefaultPort(null, portNumber, true, annotationsBuilder.build());
157 return nni;
158 }
159 return super.getPort(deviceId, portNumber);
160 }
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000161 }
162
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000163 static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS = IgmpproxyConfig.class;
164 static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS = IgmpproxySsmTranslateConfig.class;
165 static final Class<McastConfig> MCAST_CONFIG_CLASS = McastConfig.class;
166 ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
167 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
168 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
169 @Override
170 public IgmpproxyConfig createConfig() {
171 return new IgmpproxyConfig();
172 }
173 };
174
175 ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
176 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
177 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
178
179 @Override
180 public IgmpproxySsmTranslateConfig createConfig() {
181 return new IgmpproxySsmTranslateConfig();
182 }
183 };
184
185
186 class MockIgmpProxyConfig extends IgmpproxyConfig {
187 boolean igmpOnPodBasis = true;
188
189 MockIgmpProxyConfig(boolean igmpFlagValue) {
190 igmpOnPodBasis = igmpFlagValue;
191 }
192 @Override
193 public boolean igmpOnPodBasis() {
194 return igmpOnPodBasis;
195 }
196
197 @Override
198 public ConnectPoint getSourceDeviceAndPort() {
199 return COMMON_CONNECT_POINT;
200 }
201
202 @Override
203 public ConnectPoint connectPoint() {
204 return COMMON_CONNECT_POINT;
205 }
206 }
207
208
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000209 class TestNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
210 Boolean igmpOnPodFlag = false;
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000211
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000212 TestNetworkConfigRegistry(Boolean igmpFlag) {
213 igmpOnPodFlag = igmpFlag;
214 }
215 @SuppressWarnings("unchecked")
216 @Override
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800217 public <S> Set<S> getSubjects(Class<S> subjectClass) {
218 if (subjectClass.getName().equalsIgnoreCase("org.onosproject.net.DeviceId")) {
219 return (Set<S>) ImmutableSet.of(DEVICE_ID_OF_A, DEVICE_ID_OF_B);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000220 }
221 return null;
222 }
223
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800224 @SuppressWarnings("unchecked")
225 @Override
226 public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
227 if (configClass.getName().equalsIgnoreCase("org.opencord.igmpproxy.IgmpproxyConfig")) {
228 IgmpproxyConfig igmpproxyConfig = new MockIgmpProxyConfig(igmpOnPodFlag);
229 return (C) igmpproxyConfig;
230 } else {
231 super.getConfig(subject, configClass);
232 }
233 return null;
234 }
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000235 }
236
237
238 /**
239 * Keeps a reference to the PacketProcessor and saves the OutboundPackets. Adds
240 * the emitted packet in savedPackets list
241 */
242 class MockPacketService extends PacketServiceAdapter {
243
244 public MockPacketService() {
245 savedPackets = new LinkedList<>();
246 }
247
248 @Override
249 public void addProcessor(PacketProcessor processor, int priority) {
250 packetProcessor = processor;
251 }
252
253 @Override
254 public void emit(OutboundPacket packet) {
255 synchronized (savedPackets) {
256 savedPackets.add(packet);
257 savedPackets.notify();
258 }
259 }
260 }
261
262
263 class MockMastershipService extends MastershipServiceAdapter {
264 @Override
265 public boolean isLocalMaster(DeviceId deviceId) {
266 return true;
267 }
268 }
269
270 final class TestMulticastRouteService implements MulticastRouteService {
271 @Override
272 public void addListener(McastListener listener) {
273 }
274
275 @Override
276 public void removeListener(McastListener listener) {
277 }
278
279 @Override
280 public void add(McastRoute route) {
281 }
282
283 @Override
284 public void remove(McastRoute route) {
285 }
286
287 @Override
288 public Set<McastRoute> getRoutes() {
289 return null;
290 }
291
292 @Override
293 public Set<McastRoute> getRoute(IpAddress groupIp, IpAddress sourceIp) {
294 return null;
295 }
296
297 @Override
298 public void addSource(McastRoute route, HostId source) {
299 }
300
301 @Override
302 public void addSources(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints) {
303 }
304
305 @Override
306 public void addSources(McastRoute route, Set<ConnectPoint> sources) {
307
308 }
309
310 @Override
311 public void removeSources(McastRoute route) {
312
313 }
314
315 @Override
316 public void removeSource(McastRoute route, HostId source) {
317
318 }
319
320 @Override
321 public void addSink(McastRoute route, HostId hostId) {
322
323 }
324
325 @Override
326 public void addSinks(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints) {
327
328 }
329
330 @Override
331 public void addSinks(McastRoute route, Set<ConnectPoint> sinks) {
332
333 }
334
335 @Override
336 public void removeSinks(McastRoute route) {
337
338 }
339
340 @Override
341 public void removeSink(McastRoute route, HostId hostId) {
342
343 }
344
345 @Override
346 public void removeSinks(McastRoute route, Set<ConnectPoint> sink) {
347
348 }
349
350 @Override
351 public McastRouteData routeData(McastRoute route) {
352 return null;
353 }
354
355 @Override
356 public Set<ConnectPoint> sources(McastRoute route) {
357 return null;
358 }
359
360 @Override
361 public Set<ConnectPoint> sources(McastRoute route, HostId hostId) {
362 return null;
363 }
364
365 @Override
366 public Set<ConnectPoint> sinks(McastRoute route) {
367 return null;
368 }
369
370 @Override
371 public Set<ConnectPoint> sinks(McastRoute route, HostId hostId) {
372 return null;
373 }
374
375 @Override
376 public Set<ConnectPoint> nonHostSinks(McastRoute route) {
377 return null;
378 }
379
380 }
381
382
383 /**
384 * Mocks the DefaultPacketContext.
385 */
386 final class TestPacketContext extends DefaultPacketContext {
387 TestPacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
388 super(time, inPkt, outPkt, block);
389 }
390
391 @Override
392 public void send() {
393 // We don't send anything out.
394 }
395 }
396
397 /**
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000398 * Sends Ethernet packet to the process method of the Packet Processor.
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000399 *
400 * @param reply Ethernet packet
401 * @throws InterruptedException
402 */
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000403 void sendPacket(Ethernet reply, boolean isSingleSend) {
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000404
405 final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
406
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000407 if (isSingleSend) {
408 InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
409 context = new TestPacketContext(127L, inBoundPacket, null, false);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000410
411 packetProcessor.process(context);
412 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000413 if (flagForPacket) {
414 InboundPacket inPacket = new DefaultInboundPacket(CONNECT_POINT_A, reply, byteBuffer);
415 context = new TestPacketContext(127L, inPacket, null, false);
416 flagForPacket = false;
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000417
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000418 packetProcessor.process(context);
419 } else {
420 InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
421 context = new TestPacketContext(127L, inBoundPacket, null, false);
422 flagForPacket = true;
423
424 packetProcessor.process(context);
425 }
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000426 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000427
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000428 }
429
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800430 protected class MockSadisService implements SadisService {
431
432 @Override
433 public BaseInformationService<SubscriberAndDeviceInformation> getSubscriberInfoService() {
434 return new MockSubService();
435 }
436
437 @Override
438 public BaseInformationService<BandwidthProfileInformation> getBandwidthProfileService() {
439 return new MockBpService();
440 }
441 }
442
443 private class MockBpService implements BaseInformationService<BandwidthProfileInformation> {
444
445 @Override
446 public void invalidateAll() {
447
448 }
449
450 @Override
451 public void invalidateId(String id) {
452
453 }
454
455 @Override
456 public BandwidthProfileInformation get(String id) {
457 if (id.equals(dsBpId)) {
458 BandwidthProfileInformation bpInfo = new BandwidthProfileInformation();
459 bpInfo.setAssuredInformationRate(0);
460 bpInfo.setCommittedInformationRate(10000);
461 bpInfo.setCommittedBurstSize(1000L);
462 bpInfo.setExceededBurstSize(2000L);
463 bpInfo.setExceededInformationRate(20000);
464 return bpInfo;
465 }
466 return null;
467 }
468
469 @Override
470 public BandwidthProfileInformation getfromCache(String id) {
471 return null;
472 }
473 }
474
475 private class MockSubService implements BaseInformationService<SubscriberAndDeviceInformation> {
476 MockSubscriberAndDeviceInformation sub =
477 new MockSubscriberAndDeviceInformation(CLIENT_NAS_PORT_ID,
478 CLIENT_NAS_PORT_ID, CLIENT_CIRCUIT_ID, null, null);
479
480 @Override
481 public SubscriberAndDeviceInformation get(String id) {
482 return sub;
483 }
484
485 @Override
486 public void invalidateAll() {
487 }
488
489 @Override
490 public void invalidateId(String id) {
491 }
492
493 @Override
494 public SubscriberAndDeviceInformation getfromCache(String id) {
495 return null;
496 }
497 }
498
499 private class MockSubscriberAndDeviceInformation extends SubscriberAndDeviceInformation {
500
501 MockSubscriberAndDeviceInformation(String id, String nasPortId,
502 String circuitId, MacAddress hardId,
503 Ip4Address ipAddress) {
504 this.setHardwareIdentifier(hardId);
505 this.setId(id);
506 this.setIPAddress(ipAddress);
507 this.setNasPortId(nasPortId);
508 this.setCircuitId(circuitId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000509 this.setUplinkPort((int) PORT_NNI.toLong());
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800510 }
511 }
512
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000513 protected class MockCfgService implements ComponentConfigService {
514
515 @Override
516 public Set<String> getComponentNames() {
517 return null;
518 }
519
520 @Override
521 public void registerProperties(Class<?> componentClass) {
522
523 }
524
525 @Override
526 public void unregisterProperties(Class<?> componentClass, boolean clear) {
527
528 }
529
530 @Override
531 public Set<ConfigProperty> getProperties(String componentName) {
532 return null;
533 }
534
535 @Override
536 public void setProperty(String componentName, String name, String value) {
537
538 }
539
540 @Override
541 public void preSetProperty(String componentName, String name, String value) {
542
543 }
544
545 @Override
546 public void preSetProperty(String componentName, String name, String value, boolean override) {
547
548 }
549
550 @Override
551 public void unsetProperty(String componentName, String name) {
552
553 }
554
555 @Override
556 public ConfigProperty getProperty(String componentName, String attribute) {
557 return null;
558 }
559
560 }
561
562 public static class TestEventDispatcher extends DefaultEventSinkRegistry implements EventDeliveryService {
563
564 @Override
565 @SuppressWarnings("unchecked")
566 public synchronized void post(Event event) {
567 EventSink sink = getSink(event.getClass());
568 checkState(sink != null, "No sink for event %s", event);
569 sink.process(event);
570 }
571
572 @Override
573 public void setDispatchTimeLimit(long millis) {
574 }
575
576 @Override
577 public long getDispatchTimeLimit() {
578 return 0;
579 }
580 }
581
582 class MockComponentContext implements ComponentContext {
583
584 @Override
585 public Dictionary<String, Object> getProperties() {
586 Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
587 cfgDict.put("statisticsGenerationPeriodInSeconds", STATISTICS_GEN_PERIOD_IN_SEC);
588 return cfgDict;
589 }
590
591 @Override
592 public Object locateService(String name) {
593 // TODO Auto-generated method stub
594 return null;
595 }
596
597 @Override
598 public Object locateService(String name, ServiceReference reference) {
599 // TODO Auto-generated method stub
600 return null;
601 }
602
603 @Override
604 public Object[] locateServices(String name) {
605 // TODO Auto-generated method stub
606 return null;
607 }
608
609 @Override
610 public BundleContext getBundleContext() {
611 // TODO Auto-generated method stub
612 return null;
613 }
614
615 @Override
616 public Bundle getUsingBundle() {
617 // TODO Auto-generated method stub
618 return null;
619 }
620
621 @Override
622 public ComponentInstance getComponentInstance() {
623 // TODO Auto-generated method stub
624 return null;
625 }
626
627 @Override
628 public void enableComponent(String name) {
629 // TODO Auto-generated method stub
630 }
631
632 @Override
633 public void disableComponent(String name) {
634 // TODO Auto-generated method stub
635 }
636
637 @Override
638 public ServiceReference getServiceReference() {
639 // TODO Auto-generated method stub
640 return null;
641 }
642 }
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000643}