How to use SlotId class of org.openqa.selenium.grid.data package

Best Selenium code snippet using org.openqa.selenium.grid.data.SlotId

Run Selenium automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

copy
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package org.openqa.selenium.grid.distributor.local;
19
20import com.google.common.collect.ImmutableSet;
21import org.openqa.selenium.events.EventBus;
22import org.openqa.selenium.grid.data.Availability;
23import org.openqa.selenium.grid.data.NodeDrainComplete;
24import org.openqa.selenium.grid.data.NodeDrainStarted;
25import org.openqa.selenium.grid.data.NodeId;
26import org.openqa.selenium.grid.data.NodeRejectedEvent;
27import org.openqa.selenium.grid.data.NodeRemovedEvent;
28import org.openqa.selenium.grid.data.NodeStatus;
29import org.openqa.selenium.grid.data.NodeStatusEvent;
30import org.openqa.selenium.grid.data.Session;
31import org.openqa.selenium.grid.data.SessionClosedEvent;
32import org.openqa.selenium.grid.data.Slot;
33import org.openqa.selenium.grid.data.SlotId;
34import org.openqa.selenium.grid.security.Secret;
35import org.openqa.selenium.internal.Require;
36import org.openqa.selenium.remote.SessionId;
37
38import java.time.Instant;
39import java.util.HashSet;
40import java.util.Iterator;
41import java.util.Map;
42import java.util.Objects;
43import java.util.Optional;
44import java.util.Set;
45import java.util.concurrent.ConcurrentHashMap;
46import java.util.concurrent.locks.Lock;
47import java.util.concurrent.locks.ReadWriteLock;
48import java.util.concurrent.locks.ReentrantReadWriteLock;
49import java.util.logging.Logger;
50
51import static org.openqa.selenium.grid.data.Availability.DOWN;
52import static org.openqa.selenium.grid.data.Availability.DRAINING;
53import static org.openqa.selenium.grid.data.Availability.UP;
54
55public class GridModel {
56
57  private static final Logger LOG = Logger.getLogger(GridModel.class.getName());
58  private static final SessionId RESERVED = new SessionId("reserved");
59  private final ReadWriteLock lock = new ReentrantReadWriteLock(/* fair */ true);
60  private final Map<Availability, Set<NodeStatus>> nodes = new ConcurrentHashMap<>();
61  private final EventBus events;
62
63  public GridModel(EventBus events, Secret registrationSecret) {
64    this.events = Require.nonNull("Event bus", events);
65
66    events.addListener(NodeDrainStarted.listener(nodeId -> setAvailability(nodeId, DRAINING)));
67    events.addListener(NodeDrainComplete.listener(this::remove));
68    events.addListener(NodeRemovedEvent.listener(this::remove));
69    events.addListener(NodeStatusEvent.listener(status -> refresh(registrationSecret, status)));
70
71    events.addListener(SessionClosedEvent.listener(this::release));
72  }
73
74  public GridModel add(NodeStatus node) {
75    Require.nonNull("Node", node);
76
77    Lock writeLock = lock.writeLock();
78    writeLock.lock();
79    try {
80      // If we've already added the node, remove it.
81      for (Set<NodeStatus> nodes : nodes.values()) {
82        Iterator<NodeStatus> iterator = nodes.iterator();
83        while (iterator.hasNext()) {
84          NodeStatus next = iterator.next();
85
86          // If the ID is the same, we're re-adding a node. If the URI is the same a node probably restarted
87          if (next.getId().equals(node.getId()) || next.getUri().equals(node.getUri())) {
88            LOG.info(String.format("Re-adding node with id %s and URI %s.", node.getId(), node.getUri()));
89            iterator.remove();
90          }
91        }
92      }
93
94      // Nodes are initially added in the "down" state until something changes their availability
95      nodes(DOWN).add(node);
96    } finally {
97      writeLock.unlock();
98    }
99
100    return this;
101  }
102
103  public GridModel refresh(Secret registrationSecret, NodeStatus status) {
104    Require.nonNull("Node status", status);
105
106    Secret statusSecret = status.getRegistrationSecret() == null ? null : new Secret(status.getRegistrationSecret());
107    if (!Objects.equals(registrationSecret, statusSecret)) {
108      LOG.severe(String.format("Node at %s failed to send correct registration secret. Node NOT refreshed.", status.getUri()));
109      events.fire(new NodeRejectedEvent(status.getUri()));
110      return this;
111    }
112
113    Lock writeLock = lock.writeLock();
114    writeLock.lock();
115    try {
116      AvailabilityAndNode availabilityAndNode = findNode(status.getId());
117
118      if (availabilityAndNode == null) {
119        return this;
120      }
121
122      // if the node was marked as "down", keep it down until a healthcheck passes:
123      // just because the node can hit the event bus doesn't mean it's reachable
124      if (DOWN.equals(availabilityAndNode.availability)) {
125        nodes(DOWN).remove(availabilityAndNode.status);
126        nodes(DOWN).add(status);
127        return this;
128      }
129
130      // But do trust the node if it tells us it's draining
131      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
132      nodes(status.getAvailability()).add(status);
133      return this;
134    } finally {
135      writeLock.unlock();
136    }
137  }
138
139  public GridModel remove(NodeId id) {
140    Require.nonNull("Node ID", id);
141
142    Lock writeLock = lock.writeLock();
143    writeLock.lock();
144    try {
145      AvailabilityAndNode availabilityAndNode = findNode(id);
146      if (availabilityAndNode == null) {
147        return this;
148      }
149
150      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
151      return this;
152    } finally {
153      writeLock.unlock();
154    }
155  }
156
157  public Availability setAvailability(NodeId id, Availability availability) {
158    Require.nonNull("Node ID", id);
159    Require.nonNull("Availability", availability);
160
161    Lock writeLock = lock.writeLock();
162    writeLock.lock();
163    try {
164      AvailabilityAndNode availabilityAndNode = findNode(id);
165
166      if (availabilityAndNode == null) {
167        return DOWN;
168      }
169
170      if (availability.equals(availabilityAndNode.availability)) {
171        return availability;
172      }
173
174      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
175      nodes(availability).add(availabilityAndNode.status);
176
177      LOG.info(String.format(
178        "Switching node %s (uri: %s) from %s to %s",
179        id,
180        availabilityAndNode.status.getUri(),
181        availabilityAndNode.availability,
182        availability));
183      return availabilityAndNode.availability;
184    } finally {
185      writeLock.unlock();
186    }
187  }
188
189  public boolean reserve(SlotId slotId) {
190    Lock writeLock = lock.writeLock();
191    writeLock.lock();
192    try {
193      AvailabilityAndNode node = findNode(slotId.getOwningNodeId());
194      if (node == null) {
195        LOG.warning(String.format("Asked to reserve slot on node %s, but unable to find node", slotId.getOwningNodeId()));
196        return false;
197      }
198
199      if (!UP.equals(node.availability)) {
200        LOG.warning(String.format(
201          "Asked to reserve a slot on node %s, but not is %s",
202          slotId.getOwningNodeId(),
203          node.availability));
204        return false;
205      }
206
207      Optional<Slot> maybeSlot = node.status.getSlots().stream()
208        .filter(slot -> slotId.equals(slot.getId()))
209        .findFirst();
210
211      if (!maybeSlot.isPresent()) {
212        LOG.warning(String.format(
213          "Asked to reserve slot on node %s, but no slot with id %s found",
214          node.status.getId(),
215          slotId));
216        return false;
217      }
218
219      reserve(node.status, maybeSlot.get());
220      return true;
221    } finally {
222      writeLock.unlock();
223    }
224  }
225
226  public Set<NodeStatus> getSnapshot() {
227    Lock readLock = this.lock.readLock();
228    readLock.lock();
229    try {
230      ImmutableSet.Builder<NodeStatus> snapshot = ImmutableSet.builder();
231      for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
232        entry.getValue().stream()
233          .map(status -> rewrite(status, entry.getKey()))
234          .forEach(snapshot::add);
235      }
236      return snapshot.build();
237    } finally {
238      readLock.unlock();
239    }
240  }
241
242  private Set<NodeStatus> nodes(Availability availability) {
243    return nodes.computeIfAbsent(availability, ignored -> new HashSet<>());
244  }
245
246  private AvailabilityAndNode findNode(NodeId id) {
247    for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
248      for (NodeStatus nodeStatus : entry.getValue()) {
249        if (id.equals(nodeStatus.getId())) {
250          return new AvailabilityAndNode(entry.getKey(), nodeStatus);
251        }
252      }
253    }
254    return null;
255  }
256
257  private NodeStatus rewrite(NodeStatus status, Availability availability) {
258    return new NodeStatus(
259      status.getId(),
260      status.getUri(),
261      status.getMaxSessionCount(),
262      status.getSlots(),
263      availability,
264      status.getRegistrationSecret() == null ? null : new Secret(status.getRegistrationSecret()));
265  }
266
267  private void release(SessionId id) {
268    if (id == null) {
269      return;
270    }
271
272    Lock writeLock = lock.writeLock();
273    writeLock.lock();
274    try {
275      for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
276        for (NodeStatus node : entry.getValue()) {
277          for (Slot slot : node.getSlots()) {
278            if (!slot.getSession().isPresent()) {
279              continue;
280            }
281
282            if (id.equals(slot.getSession().get().getId())) {
283              Slot released = new Slot(
284                slot.getId(),
285                slot.getStereotype(),
286                slot.getLastStarted(),
287                Optional.empty());
288              amend(entry.getKey(), node, released);
289              return;
290            }
291          }
292        }
293      }
294    } finally {
295      writeLock.unlock();
296    }
297  }
298
299  private void reserve(NodeStatus status, Slot slot) {
300    Instant now = Instant.now();
301
302    Slot reserved = new Slot(
303      slot.getId(),
304      slot.getStereotype(),
305      now,
306      Optional.of(new Session(
307        RESERVED,
308        status.getUri(),
309        slot.getStereotype(),
310        slot.getStereotype(),
311        now)));
312
313    amend(UP, status, reserved);
314  }
315
316  public void setSession(SlotId slotId, Session session) {
317    Require.nonNull("Slot ID", slotId);
318
319    AvailabilityAndNode node = findNode(slotId.getOwningNodeId());
320    if (node == null) {
321      LOG.warning("Grid model and reality have diverged. Unable to find node " + slotId.getOwningNodeId());
322      return;
323    }
324
325    Optional<Slot> maybeSlot = node.status.getSlots().stream()
326      .filter(slot -> slotId.equals(slot.getId()))
327      .findFirst();
328
329    if (!maybeSlot.isPresent()) {
330      LOG.warning("Grid model and reality have diverged. Unable to find slot " + slotId);
331      return;
332    }
333
334    Slot slot = maybeSlot.get();
335    Optional<Session> maybeSession = slot.getSession();
336    if (!maybeSession.isPresent()) {
337      LOG.warning("Grid model and reality have diverged. Slot is not reserved. " + slotId);
338      return;
339    }
340
341    Session current = maybeSession.get();
342    if (!RESERVED.equals(current.getId())) {
343      LOG.warning("Gid model and reality have diverged. Slot has session and is not reserved. " + slotId);
344      return;
345    }
346
347    Slot updated = new Slot(
348      slot.getId(),
349      slot.getStereotype(),
350      session == null ? slot.getLastStarted() : session.getStartTime(),
351      Optional.ofNullable(session));
352
353    amend(node.availability, node.status, updated);
354  }
355
356  private void amend(Availability availability, NodeStatus status, Slot slot) {
357    Set<Slot> newSlots = new HashSet<>(status.getSlots());
358    newSlots.removeIf(s -> s.getId().equals(slot.getId()));
359    newSlots.add(slot);
360
361    nodes(availability).remove(status);
362    nodes(availability).add(new NodeStatus(
363      status.getId(),
364      status.getUri(),
365      status.getMaxSessionCount(),
366      newSlots,
367      status.getAvailability(),
368      status.getRegistrationSecret() == null ? null : new Secret(status.getRegistrationSecret())));
369  }
370
371  private static class AvailabilityAndNode {
372    public final Availability availability;
373    public final NodeStatus status;
374
375    public AvailabilityAndNode(Availability availability, NodeStatus status) {
376      this.availability = availability;
377      this.status = status;
378    }
379  }
380}
381
Full Screen
copy
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package org.openqa.selenium.grid.distributor.local;
19
20import com.google.common.collect.ImmutableSet;
21import org.openqa.selenium.Beta;
22import org.openqa.selenium.Capabilities;
23import org.openqa.selenium.ImmutableCapabilities;
24import org.openqa.selenium.SessionNotCreatedException;
25import org.openqa.selenium.concurrent.Regularly;
26import org.openqa.selenium.events.EventBus;
27import org.openqa.selenium.grid.config.Config;
28import org.openqa.selenium.grid.data.CreateSessionRequest;
29import org.openqa.selenium.grid.data.CreateSessionResponse;
30import org.openqa.selenium.grid.data.DistributorStatus;
31import org.openqa.selenium.grid.data.NodeAddedEvent;
32import org.openqa.selenium.grid.data.NodeDrainComplete;
33import org.openqa.selenium.grid.data.NodeId;
34import org.openqa.selenium.grid.data.NodeRejectedEvent;
35import org.openqa.selenium.grid.data.NodeRemovedEvent;
36import org.openqa.selenium.grid.data.NodeStatus;
37import org.openqa.selenium.grid.data.NodeStatusEvent;
38import org.openqa.selenium.grid.data.Slot;
39import org.openqa.selenium.grid.data.SlotId;
40import org.openqa.selenium.grid.distributor.Distributor;
41import org.openqa.selenium.grid.distributor.selector.DefaultSlotSelector;
42import org.openqa.selenium.grid.log.LoggingOptions;
43import org.openqa.selenium.grid.node.HealthCheck;
44import org.openqa.selenium.grid.node.Node;
45import org.openqa.selenium.grid.node.remote.RemoteNode;
46import org.openqa.selenium.grid.security.Secret;
47import org.openqa.selenium.grid.server.BaseServerOptions;
48import org.openqa.selenium.grid.server.EventBusOptions;
49import org.openqa.selenium.grid.server.NetworkOptions;
50import org.openqa.selenium.grid.sessionmap.SessionMap;
51import org.openqa.selenium.grid.sessionmap.config.SessionMapOptions;
52import org.openqa.selenium.internal.Require;
53import org.openqa.selenium.remote.http.HttpClient;
54import org.openqa.selenium.remote.tracing.Tracer;
55import org.openqa.selenium.status.HasReadyState;
56
57import java.time.Duration;
58import java.util.ArrayList;
59import java.util.HashMap;
60import java.util.List;
61import java.util.Map;
62import java.util.Objects;
63import java.util.Optional;
64import java.util.Set;
65import java.util.concurrent.locks.Lock;
66import java.util.concurrent.locks.ReadWriteLock;
67import java.util.concurrent.locks.ReentrantReadWriteLock;
68import java.util.function.Supplier;
69import java.util.logging.Level;
70import java.util.logging.Logger;
71
72import static com.google.common.collect.ImmutableSet.toImmutableSet;
73import static org.openqa.selenium.grid.data.Availability.DOWN;
74import static org.openqa.selenium.grid.data.Availability.DRAINING;
75
76public class LocalDistributor extends Distributor {
77
78  private static final Logger LOG = Logger.getLogger(LocalDistributor.class.getName());
79
80  private final Tracer tracer;
81  private final EventBus bus;
82  private final HttpClient.Factory clientFactory;
83  private final SessionMap sessions;
84  private final Regularly hostChecker = new Regularly("distributor host checker");
85  private final Map<NodeId, Runnable> allChecks = new HashMap<>();
86
87  private final ReadWriteLock lock = new ReentrantReadWriteLock(/* fair */ true);
88  private final GridModel model;
89  private final Map<NodeId, Node> nodes;
90
91  public LocalDistributor(
92      Tracer tracer,
93      EventBus bus,
94      HttpClient.Factory clientFactory,
95      SessionMap sessions,
96      Secret registrationSecret) {
97    super(tracer, clientFactory, new DefaultSlotSelector(), sessions, registrationSecret);
98    this.tracer = Require.nonNull("Tracer", tracer);
99    this.bus = Require.nonNull("Event bus", bus);
100    this.clientFactory = Require.nonNull("HTTP client factory", clientFactory);
101    this.sessions = Require.nonNull("Session map", sessions);
102    this.model = new GridModel(bus, registrationSecret);
103    this.nodes = new HashMap<>();
104
105    bus.addListener(NodeStatusEvent.listener(status -> register(registrationSecret, status)));
106    bus.addListener(NodeStatusEvent.listener(status -> model.refresh(registrationSecret, status)));
107    bus.addListener(NodeDrainComplete.listener(this::remove));
108  }
109
110  public static Distributor create(Config config) {
111    Tracer tracer = new LoggingOptions(config).getTracer();
112    EventBus bus = new EventBusOptions(config).getEventBus();
113    HttpClient.Factory clientFactory = new NetworkOptions(config).getHttpClientFactory(tracer);
114    SessionMap sessions = new SessionMapOptions(config).getSessionMap();
115    BaseServerOptions serverOptions = new BaseServerOptions(config);
116
117    return new LocalDistributor(tracer, bus, clientFactory, sessions, serverOptions.getRegistrationSecret());
118  }
119
120  @Override
121  public boolean isReady() {
122    try {
123      return ImmutableSet.of(bus, sessions).parallelStream()
124        .map(HasReadyState::isReady)
125        .reduce(true, Boolean::logicalAnd);
126    } catch (RuntimeException e) {
127      return false;
128    }
129  }
130
131  private void register(Secret registrationSecret, NodeStatus status) {
132    Require.nonNull("Node", status);
133
134    Secret nodeSecret = status.getRegistrationSecret() == null ? null : new Secret(status.getRegistrationSecret());
135    if (!Objects.equals(registrationSecret, nodeSecret)) {
136      LOG.severe(String.format("Node at %s failed to send correct registration secret. Node NOT registered.", status.getUri()));
137      bus.fire(new NodeRejectedEvent(status.getUri()));
138      return;
139    }
140
141    Lock writeLock = lock.writeLock();
142    writeLock.lock();
143    try {
144      if (nodes.containsKey(status.getId())) {
145        return;
146      }
147
148      Set<Capabilities> capabilities = status.getSlots().stream()
149        .map(Slot::getStereotype)
150        .map(ImmutableCapabilities::copyOf)
151        .collect(toImmutableSet());
152
153      // A new node! Add this as a remote node, since we've not called add
154      RemoteNode remoteNode = new RemoteNode(
155        tracer,
156        clientFactory,
157        status.getId(),
158        status.getUri(),
159        registrationSecret,
160        capabilities);
161
162      add(remoteNode);
163    } finally {
164      writeLock.unlock();
165    }
166  }
167
168  @Override
169  public LocalDistributor add(Node node) {
170    Require.nonNull("Node", node);
171
172    LOG.info(String.format("Added node %s at %s.", node.getId(), node.getUri()));
173
174    nodes.put(node.getId(), node);
175    model.add(node.getStatus());
176
177    // Extract the health check
178    Runnable runnableHealthCheck = asRunnableHealthCheck(node);
179    allChecks.put(node.getId(), runnableHealthCheck);
180    hostChecker.submit(runnableHealthCheck, Duration.ofMinutes(5), Duration.ofSeconds(30));
181
182    bus.fire(new NodeAddedEvent(node.getId()));
183
184    return this;
185  }
186
187  private Runnable asRunnableHealthCheck(Node node) {
188    HealthCheck healthCheck = node.getHealthCheck();
189    NodeId id = node.getId();
190    return () -> {
191      HealthCheck.Result result;
192      try {
193        result = healthCheck.check();
194      } catch (Exception e) {
195        LOG.log(Level.WARNING, "Unable to process node " + id, e);
196        result = new HealthCheck.Result(DOWN, "Unable to run healthcheck. Assuming down");
197      }
198
199      Lock writeLock = lock.writeLock();
200      writeLock.lock();
201      try {
202        model.setAvailability(id, result.getAvailability());
203      } finally {
204        writeLock.unlock();
205      }
206    };
207  }
208
209  @Override
210  public boolean drain(NodeId nodeId) {
211    Node node = nodes.get(nodeId);
212    if (node == null) {
213      LOG.info("Asked to drain unregistered node " + nodeId);
214      return false;
215    }
216
217    Lock writeLock = lock.writeLock();
218    writeLock.lock();
219    try {
220      node.drain();
221      model.setAvailability(nodeId, DRAINING);
222    } finally {
223      writeLock.unlock();
224    }
225
226    return node.isDraining();
227  }
228
229  public void remove(NodeId nodeId) {
230    Lock writeLock = lock.writeLock();
231    writeLock.lock();
232    try {
233      model.remove(nodeId);
234      Runnable runnable = allChecks.remove(nodeId);
235      if (runnable != null) {
236        hostChecker.remove(runnable);
237      }
238    } finally {
239      writeLock.unlock();
240      bus.fire(new NodeRemovedEvent(nodeId));
241    }
242  }
243
244  @Override
245  public DistributorStatus getStatus() {
246    Lock readLock = this.lock.readLock();
247    readLock.lock();
248    try {
249      return new DistributorStatus(model.getSnapshot());
250    } finally {
251      readLock.unlock();
252    }
253  }
254
255  @Beta
256  public void refresh() {
257    List<Runnable> allHealthChecks = new ArrayList<>();
258
259    Lock readLock = this.lock.readLock();
260    readLock.lock();
261    try {
262      allHealthChecks.addAll(allChecks.values());
263    } finally {
264      readLock.unlock();
265    }
266
267    allHealthChecks.parallelStream().forEach(Runnable::run);
268  }
269
270  @Override
271  protected Set<NodeStatus> getAvailableNodes() {
272    Lock readLock = this.lock.readLock();
273    readLock.lock();
274    try {
275      return model.getSnapshot().stream()
276        .filter(node -> !DOWN.equals(node.getAvailability()))
277        .collect(toImmutableSet());
278    } finally {
279      readLock.unlock();
280    }
281  }
282
283  @Override
284  protected Supplier<CreateSessionResponse> reserve(SlotId slotId, CreateSessionRequest request) {
285    Require.nonNull("Slot ID", slotId);
286    Require.nonNull("New Session request", request);
287
288    Lock writeLock = this.lock.writeLock();
289    writeLock.lock();
290    try {
291      Node node = nodes.get(slotId.getOwningNodeId());
292      if (node == null) {
293        return () -> {
294          throw new SessionNotCreatedException("Unable to find node");
295        };
296      }
297
298      model.reserve(slotId);
299
300      return () -> {
301        Optional<CreateSessionResponse> response = node.newSession(request);
302
303        if (!response.isPresent()) {
304          model.setSession(slotId, null);
305          throw new SessionNotCreatedException("Unable to create session for " + request);
306        }
307
308        model.setSession(slotId, response.get().getSession());
309
310        return response.get();
311      };
312
313    } finally {
314      writeLock.unlock();
315    }
316  }
317}
318
Full Screen
copy
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package org.openqa.selenium.grid.node.k8s;
19
20import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.ImmutableSet;
22import org.openqa.selenium.Capabilities;
23import org.openqa.selenium.ImmutableCapabilities;
24import org.openqa.selenium.NoSuchSessionException;
25import org.openqa.selenium.PersistentCapabilities;
26import org.openqa.selenium.WebDriver;
27import org.openqa.selenium.WebDriverInfo;
28import org.openqa.selenium.events.EventBus;
29import org.openqa.selenium.grid.config.Config;
30import org.openqa.selenium.grid.config.ConfigException;
31import org.openqa.selenium.grid.data.CreateSessionRequest;
32import org.openqa.selenium.grid.data.CreateSessionResponse;
33import org.openqa.selenium.grid.data.NodeDrainComplete;
34import org.openqa.selenium.grid.data.NodeDrainStarted;
35import org.openqa.selenium.grid.data.NodeId;
36import org.openqa.selenium.grid.data.NodeStatus;
37import org.openqa.selenium.grid.data.Session;
38import org.openqa.selenium.grid.data.SessionClosedEvent;
39import org.openqa.selenium.grid.data.Slot;
40import org.openqa.selenium.grid.data.SlotId;
41import org.openqa.selenium.grid.log.LoggingOptions;
42import org.openqa.selenium.grid.node.HealthCheck;
43import org.openqa.selenium.grid.node.Node;
44import org.openqa.selenium.grid.node.config.NodeOptions;
45import org.openqa.selenium.grid.security.Secret;
46import org.openqa.selenium.grid.server.BaseServerOptions;
47import org.openqa.selenium.grid.server.EventBusOptions;
48import org.openqa.selenium.internal.Require;
49import org.openqa.selenium.json.Json;
50import org.openqa.selenium.remote.CommandExecutor;
51import org.openqa.selenium.remote.RemoteWebDriver;
52import org.openqa.selenium.remote.SessionId;
53import org.openqa.selenium.remote.http.HttpClient;
54import org.openqa.selenium.remote.http.HttpRequest;
55import org.openqa.selenium.remote.http.HttpResponse;
56import org.openqa.selenium.remote.tracing.Tracer;
57
58import java.lang.reflect.Field;
59import java.net.URI;
60import java.net.URISyntaxException;
61import java.time.Instant;
62import java.util.HashMap;
63import java.util.Map;
64import java.util.Optional;
65import java.util.ServiceLoader;
66import java.util.TreeMap;
67import java.util.UUID;
68import java.util.logging.Logger;
69import java.util.stream.StreamSupport;
70
71import static java.nio.charset.StandardCharsets.UTF_8;
72import static org.openqa.selenium.grid.data.Availability.DRAINING;
73import static org.openqa.selenium.grid.data.Availability.UP;
74import static org.openqa.selenium.json.Json.MAP_TYPE;
75import static org.openqa.selenium.remote.http.HttpMethod.DELETE;
76
77/**
78 * An implementation of {@link Node} that marks itself as draining immediately
79 * after starting, and which then shuts down after usage. This will allow an
80 * appropriately configured Kubernetes cluster to start a new node once the
81 * session is finished.
82 */
83public class OneShotNode extends Node {
84
85  private static final Logger LOG = Logger.getLogger(OneShotNode.class.getName());
86  private static final Json JSON = new Json();
87
88  private final EventBus events;
89  private final WebDriverInfo driverInfo;
90  private final Capabilities stereotype;
91  private final Secret registrationSecret;
92  private final URI gridUri;
93  private final UUID slotId = UUID.randomUUID();
94  private RemoteWebDriver driver;
95  private SessionId sessionId;
96  private HttpClient client;
97  private Capabilities capabilities;
98  private Instant sessionStart = Instant.EPOCH;
99
100  private OneShotNode(
101    Tracer tracer,
102    EventBus events,
103    Secret registrationSecret,
104    NodeId id,
105    URI uri,
106    URI gridUri,
107    Capabilities stereotype,
108    WebDriverInfo driverInfo) {
109    super(tracer, id, uri, registrationSecret);
110
111    this.registrationSecret = registrationSecret;
112    this.events = Require.nonNull("Event bus", events);
113    this.gridUri = Require.nonNull("Public Grid URI", gridUri);
114    this.stereotype = ImmutableCapabilities.copyOf(Require.nonNull("Stereotype", stereotype));
115    this.driverInfo = Require.nonNull("Driver info", driverInfo);
116  }
117
118  public static Node create(Config config) {
119    LoggingOptions loggingOptions = new LoggingOptions(config);
120    EventBusOptions eventOptions = new EventBusOptions(config);
121    BaseServerOptions serverOptions = new BaseServerOptions(config);
122    NodeOptions nodeOptions = new NodeOptions(config);
123
124    Map<String, Object> raw = new Json().toType(
125      config.get("k8s", "stereotype")
126        .orElseThrow(() -> new ConfigException("Unable to find node stereotype")),
127      MAP_TYPE);
128
129    Capabilities stereotype = new ImmutableCapabilities(raw);
130
131    Optional<String> driverName = config.get("k8s", "driver_name").map(String::toLowerCase);
132
133    // Find the webdriver info corresponding to the driver name
134    WebDriverInfo driverInfo = StreamSupport.stream(ServiceLoader.load(WebDriverInfo.class).spliterator(), false)
135      .filter(info -> info.isSupporting(stereotype))
136      .filter(info -> driverName.map(name -> name.equals(info.getDisplayName().toLowerCase())).orElse(true))
137      .findFirst()
138      .orElseThrow(() -> new ConfigException(
139        "Unable to find matching driver for %s and %s", stereotype, driverName.orElse("any driver")));
140
141    LOG.info(String.format("Creating one-shot node for %s with stereotype %s", driverInfo, stereotype));
142    LOG.info("Grid URI is: " + nodeOptions.getPublicGridUri());
143
144    return new OneShotNode(
145      loggingOptions.getTracer(),
146      eventOptions.getEventBus(),
147      serverOptions.getRegistrationSecret(),
148      new NodeId(UUID.randomUUID()),
149      serverOptions.getExternalUri(),
150      nodeOptions.getPublicGridUri().orElseThrow(() -> new ConfigException("Unable to determine public grid address")),
151      stereotype,
152      driverInfo);
153  }
154
155  @Override
156  public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {
157    if (driver != null) {
158      throw new IllegalStateException("Only expected one session at a time");
159    }
160
161    Optional<WebDriver> driver = driverInfo.createDriver(sessionRequest.getCapabilities());
162    if (!driver.isPresent()) {
163      return Optional.empty();
164    }
165
166    if (!(driver.get() instanceof RemoteWebDriver)) {
167      driver.get().quit();
168      return Optional.empty();
169    }
170
171    this.driver = (RemoteWebDriver) driver.get();
172    this.sessionId = this.driver.getSessionId();
173    this.client = extractHttpClient(this.driver);
174    this.capabilities = rewriteCapabilities(this.driver);
175    this.sessionStart = Instant.now();
176
177    LOG.info("Encoded response: " + JSON.toJson(ImmutableMap.of(
178      "value", ImmutableMap.of(
179        "sessionId", sessionId,
180        "capabilities", capabilities))));
181
182    events.fire(new NodeDrainStarted(getId()));
183
184    return Optional.of(
185      new CreateSessionResponse(
186        getSession(sessionId),
187        JSON.toJson(ImmutableMap.of(
188          "value", ImmutableMap.of(
189            "sessionId", sessionId,
190            "capabilities", capabilities))).getBytes(UTF_8)));
191  }
192
193  private HttpClient extractHttpClient(RemoteWebDriver driver) {
194    CommandExecutor executor = driver.getCommandExecutor();
195
196    try {
197      Field client = null;
198      Class<?> current = executor.getClass();
199      while (client == null && (current != null || Object.class.equals(current))) {
200        client = findClientField(current);
201        current = current.getSuperclass();
202      }
203
204      if (client == null) {
205        throw new IllegalStateException("Unable to find client field in " + executor.getClass());
206      }
207
208      if (!HttpClient.class.isAssignableFrom(client.getType())) {
209        throw new IllegalStateException("Client field is not assignable to http client");
210      }
211      client.setAccessible(true);
212      return (HttpClient) client.get(executor);
213    } catch (ReflectiveOperationException e) {
214      throw new IllegalStateException(e);
215    }
216  }
217
218  private Field findClientField(Class<?> clazz) {
219    try {
220      return clazz.getDeclaredField("client");
221    } catch (NoSuchFieldException e) {
222      return null;
223    }
224  }
225
226  private Capabilities rewriteCapabilities(RemoteWebDriver driver) {
227    // Rewrite the se:options if necessary
228    Object rawSeleniumOptions = driver.getCapabilities().getCapability("se:options");
229    if (rawSeleniumOptions == null || rawSeleniumOptions instanceof Map) {
230      @SuppressWarnings("unchecked") Map<String, Object> original = (Map<String, Object>) rawSeleniumOptions;
231      Map<String, Object> updated = new TreeMap<>(original == null ? new HashMap<>() : original);
232
233      String cdpPath = String.format("/session/%s/se/cdp", driver.getSessionId());
234      updated.put("cdp", rewrite(cdpPath));
235
236      return new PersistentCapabilities(driver.getCapabilities()).setCapability("se:options", updated);
237    }
238
239    return ImmutableCapabilities.copyOf(driver.getCapabilities());
240  }
241
242  private URI rewrite(String path) {
243    try {
244      return new URI(
245        gridUri.getScheme(),
246        gridUri.getUserInfo(),
247        gridUri.getHost(),
248        gridUri.getPort(),
249        path,
250        null,
251        null);
252    } catch (URISyntaxException e) {
253      throw new RuntimeException(e);
254    }
255  }
256
257  @Override
258  public HttpResponse executeWebDriverCommand(HttpRequest req) {
259    LOG.info("Executing " + req);
260
261    HttpResponse res = client.execute(req);
262
263    if (DELETE.equals(req.getMethod()) && req.getUri().equals("/session/" + sessionId)) {
264      // Ensure the response is sent before we viciously kill the node
265
266      new Thread(
267        () -> {
268          try {
269            Thread.sleep(500);
270          } catch (InterruptedException e) {
271            Thread.currentThread().interrupt();
272            throw new RuntimeException(e);
273          }
274          LOG.info("Stopping session: " + sessionId);
275          stop(sessionId);
276        },
277        "Node clean up: " + getId())
278      .start();
279    }
280
281    return res;
282  }
283
284  @Override
285  public Session getSession(SessionId id) throws NoSuchSessionException {
286    if (!isSessionOwner(id)) {
287      throw new NoSuchSessionException("Unable to find session with id: " + id);
288    }
289
290    return new Session(
291      sessionId,
292      getUri(),
293      stereotype,
294      capabilities,
295      sessionStart); }
296
297  @Override
298  public HttpResponse uploadFile(HttpRequest req, SessionId id) {
299    return null;
300  }
301
302  @Override
303  public void stop(SessionId id) throws NoSuchSessionException {
304    LOG.info("Stop has been called: " + id);
305    Require.nonNull("Session ID", id);
306
307    if (!isSessionOwner(id)) {
308      throw new NoSuchSessionException("Unable to find session " + id);
309    }
310
311    LOG.info("Quitting session " + id);
312    try {
313      driver.quit();
314    } catch (Exception e) {
315      // It's possible that the driver has already quit.
316    }
317
318    events.fire(new SessionClosedEvent(id));
319    LOG.info("Firing node drain complete message");
320    events.fire(new NodeDrainComplete(getId()));
321  }
322
323  @Override
324  public boolean isSessionOwner(SessionId id) {
325    return driver != null && sessionId.equals(id);
326  }
327
328  @Override
329  public boolean isSupporting(Capabilities capabilities) {
330    return driverInfo.isSupporting(capabilities);
331  }
332
333  @Override
334  public NodeStatus getStatus() {
335    return new NodeStatus(
336      getId(),
337      getUri(),
338      1,
339      ImmutableSet.of(
340        new Slot(
341          new SlotId(getId(), slotId),
342          stereotype,
343          Instant.EPOCH,
344          driver == null ?
345            Optional.empty() :
346            Optional.of(new Session(sessionId, getUri(), stereotype, capabilities, Instant.now())))),
347      isDraining() ? DRAINING : UP,
348      registrationSecret);
349  }
350
351  @Override
352  public void drain() {
353    events.fire(new NodeDrainStarted(getId()));
354    draining = true;
355  }
356
357  @Override
358  public HealthCheck getHealthCheck() {
359    return () -> new HealthCheck.Result(isDraining() ? DRAINING : UP, "Everything is fine");
360  }
361
362  @Override
363  public boolean isReady() {
364    return events.isReady();
365  }
366}
367
Full Screen
copy
1public class IncomingCallInterceptor extends BroadcastReceiver {
2
3    @Override
4    public void onReceive(Context context, Intent intent) {
5    String callingSIM = "";
6    Bundle bundle = intent.getExtras();
7    callingSIM = String.valueOf(bundle.getInt("simId", -1));
8
9    if(callingSIM.equals("0")){
10           // Incoming call from SIM1
11        } else if(callingSIM.equals("1")){
12           // Incoming call from SIM2
13        }
14    }
15}
16
Full Screen
copy
1<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
2
Full Screen
copy
1String configName = "/etc/pkcs11_java.cfg";
2Provider p = new sun.security.pkcs11.SunPKCS11(configName);
3keyStore = KeyStore.getInstance("PKCS11", p);
4
Full Screen
copy
1name=OpenSC
2description = SunPKCS11 via OpenSC
3library=/usr/local/lib/opensc-pkcs11.so
4
Full Screen
copy
1keytool -providerClass sun.security.pkcs11.SunPKCS11 \
2  -providerArg /home/hans/Desktop/smartcards/opensc-java.cfg \
3  -providerName SunPKCS11-OpenSC -keystore NONE -storetype PKCS11 \
4  -list \
5  -J-Djava.security.debug=sunpkcs11
6
Full Screen
copy
1name = OpenSC
2description = SunPKCS11 w/ OpenSC Smart card Framework
3library = /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
4slot = 2
5
Full Screen
copy
1    long slotID = config.getSlotID();
2    // ....
3        if ((slotID < 0) || showInfo) {
4            long[] slots = p11.C_GetSlotList(false);
5            if (showInfo) {
6                System.out.println("All slots: " + toString(slots));
7                slots = p11.C_GetSlotList(true);
8                System.out.println("Slots with tokens: " + toString(slots));
9            }
10            if (slotID < 0) {
11                if ((slotListIndex < 0) || (slotListIndex >= slots.length)) {
12                    throw new ProviderException("slotListIndex is " + slotListIndex
13                        + " but token only has " + slots.length + " slots");
14                }
15                slotID = slots[slotListIndex];
16            }
17        }
18        this.slotID = slotID;
19
Full Screen
copy
1SubscriptionManager localSubscriptionManager = SubscriptionManager.from(context);
2            if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) {
3//do you stuff here
4}
5
Full Screen
copy
1public static boolean sendSMS(Context context, int simID, String toNum, String centerNum, String smsText, PendingIntent sentIntent, PendingIntent deliveryIntent) {
2    SmsManager smsMan = SmsManager.getDefault();
3    int[] param;
4    Method method = null;
5    try {
6        SubscriptionManager localSubscriptionManager = SubscriptionManager.from(context);
7        if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) {
8            List localList = localSubscriptionManager.getActiveSubscriptionInfoList();
9            SubscriptionInfo simInfo = (SubscriptionInfo) localList.get(simID);
10            SmsManager.getSmsManagerForSubscriptionId(simInfo.getSubscriptionId()).sendTextMessage(toNum, null, smsText, null, null);
11        }
12        return true;
13    } catch (Exception e) {
14        Log.e("SmsSimUtil.sendSMS", "Exception:" + e.getMessage());
15    }
16    return false;
17}
18
Full Screen
copy
1<uses-permission android:name="android.permission.SEND_SMS"/>
2<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
3
Full Screen
copy
1import android.app.Activity;
2import android.os.Bundle;
3import android.widget.TextView;
4
5public class MainActivity extends Activity {
6
7    @Override
8    protected void onCreate(Bundle savedInstanceState) {
9        super.onCreate(savedInstanceState);
10        setContentView(R.layout.activity_main);
11
12        TelephonyInfo telephonyInfo = TelephonyInfo.getInstance(this);
13
14        String imeiSIM1 = telephonyInfo.getImeiSIM1();
15        String imeiSIM2 = telephonyInfo.getImeiSIM2();
16
17        boolean isSIM1Ready = telephonyInfo.isSIM1Ready();
18        boolean isSIM2Ready = telephonyInfo.isSIM2Ready();
19
20        boolean isDualSIM = telephonyInfo.isDualSIM();
21
22        TextView tv = (TextView) findViewById(R.id.tv);
23        tv.setText(" IME1 : " + imeiSIM1 + "\n" +
24                " IME2 : " + imeiSIM2 + "\n" +
25                " IS DUAL SIM : " + isDualSIM + "\n" +
26                " IS SIM1 READY : " + isSIM1Ready + "\n" +
27                " IS SIM2 READY : " + isSIM2Ready + "\n");
28    }
29}
30
Full Screen
copy
1import java.lang.reflect.Method;
2
3import android.content.Context;
4import android.telephony.TelephonyManager;
5
6public final class TelephonyInfo {
7
8    private static TelephonyInfo telephonyInfo;
9    private String imeiSIM1;
10    private String imeiSIM2;
11    private boolean isSIM1Ready;
12    private boolean isSIM2Ready;
13
14    public String getImeiSIM1() {
15        return imeiSIM1;
16    }
17
18    /*public static void setImeiSIM1(String imeiSIM1) {
19        TelephonyInfo.imeiSIM1 = imeiSIM1;
20    }*/
21
22    public String getImeiSIM2() {
23        return imeiSIM2;
24    }
25
26    /*public static void setImeiSIM2(String imeiSIM2) {
27        TelephonyInfo.imeiSIM2 = imeiSIM2;
28    }*/
29
30    public boolean isSIM1Ready() {
31        return isSIM1Ready;
32    }
33
34    /*public static void setSIM1Ready(boolean isSIM1Ready) {
35        TelephonyInfo.isSIM1Ready = isSIM1Ready;
36    }*/
37
38    public boolean isSIM2Ready() {
39        return isSIM2Ready;
40    }
41
42    /*public static void setSIM2Ready(boolean isSIM2Ready) {
43        TelephonyInfo.isSIM2Ready = isSIM2Ready;
44    }*/
45
46    public boolean isDualSIM() {
47        return imeiSIM2 != null;
48    }
49
50    private TelephonyInfo() {
51    }
52
53    public static TelephonyInfo getInstance(Context context){
54
55        if(telephonyInfo == null) {
56
57            telephonyInfo = new TelephonyInfo();
58
59            TelephonyManager telephonyManager = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
60
61            telephonyInfo.imeiSIM1 = telephonyManager.getDeviceId();;
62            telephonyInfo.imeiSIM2 = null;
63
64            try {
65                telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdGemini", 0);
66                telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdGemini", 1);
67            } catch (GeminiMethodNotFoundException e) {
68                e.printStackTrace();
69
70                try {
71                    telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceId", 0);
72                    telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceId", 1);
73                } catch (GeminiMethodNotFoundException e1) {
74                    //Call here for next manufacturer's predicted method name if you wish
75                    e1.printStackTrace();
76                }
77            }
78
79            telephonyInfo.isSIM1Ready = telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY;
80            telephonyInfo.isSIM2Ready = false;
81
82            try {
83                telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimStateGemini", 0);
84                telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimStateGemini", 1);
85            } catch (GeminiMethodNotFoundException e) {
86
87                e.printStackTrace();
88
89                try {
90                    telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimState", 0);
91                    telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimState", 1);
92                } catch (GeminiMethodNotFoundException e1) {
93                    //Call here for next manufacturer's predicted method name if you wish
94                    e1.printStackTrace();
95                }
96            }
97        }
98
99        return telephonyInfo;
100    }
101
102    private static String getDeviceIdBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {
103
104        String imei = null;
105
106        TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
107
108        try{
109
110            Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
111
112            Class<?>[] parameter = new Class[1];
113            parameter[0] = int.class;
114            Method getSimID = telephonyClass.getMethod(predictedMethodName, parameter);
115
116            Object[] obParameter = new Object[1];
117            obParameter[0] = slotID;
118            Object ob_phone = getSimID.invoke(telephony, obParameter);
119
120            if(ob_phone != null){
121                imei = ob_phone.toString();
122
123            }
124        } catch (Exception e) {
125            e.printStackTrace();
126            throw new GeminiMethodNotFoundException(predictedMethodName);
127        }
128
129        return imei;
130    }
131
132    private static  boolean getSIMStateBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {
133
134        boolean isReady = false;
135
136        TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
137
138        try{
139
140            Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
141
142            Class<?>[] parameter = new Class[1];
143            parameter[0] = int.class;
144            Method getSimStateGemini = telephonyClass.getMethod(predictedMethodName, parameter);
145
146            Object[] obParameter = new Object[1];
147            obParameter[0] = slotID;
148            Object ob_phone = getSimStateGemini.invoke(telephony, obParameter);
149
150            if(ob_phone != null){
151                int simState = Integer.parseInt(ob_phone.toString());
152                if(simState == TelephonyManager.SIM_STATE_READY){
153                    isReady = true;
154                }
155            }
156        } catch (Exception e) {
157            e.printStackTrace();
158            throw new GeminiMethodNotFoundException(predictedMethodName);
159        }
160
161        return isReady;
162    }
163
164
165    private static class GeminiMethodNotFoundException extends Exception {
166
167        private static final long serialVersionUID = -996812356902545308L;
168
169        public GeminiMethodNotFoundException(String info) {
170            super(info);
171        }
172    }
173}
174
Full Screen
copy
1public static void printTelephonyManagerMethodNamesForThisDevice(Context context) {
2
3    TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
4    Class<?> telephonyClass;
5    try {
6        telephonyClass = Class.forName(telephony.getClass().getName());
7        Method[] methods = telephonyClass.getMethods();
8        for (int idx = 0; idx < methods.length; idx++) {
9
10            System.out.println("\n" + methods[idx] + " declared by " + methods[idx].getDeclaringClass());
11        }
12    } catch (ClassNotFoundException e) {
13        e.printStackTrace();
14    }
15} 
16
Full Screen
copy
1telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdDs", 0);
2telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdDs", 1); 
3
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Most used methods in SlotId

Run Selenium Automation Tests on LambdaTest Cloud Grid

Trigger Selenium automation tests on a cloud-based Grid of 3000+ real browsers and operating systems.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)