• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.telephony.dataconnection;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.KeepalivePacketData;
22 import android.net.LinkProperties;
23 import android.net.NattKeepalivePacketData;
24 import android.net.NetworkAgent;
25 import android.net.NetworkAgentConfig;
26 import android.net.NetworkCapabilities;
27 import android.net.NetworkProvider;
28 import android.net.QosFilter;
29 import android.net.QosSessionAttributes;
30 import android.net.SocketKeepalive;
31 import android.net.Uri;
32 import android.os.Message;
33 import android.telephony.AccessNetworkConstants;
34 import android.telephony.AccessNetworkConstants.TransportType;
35 import android.telephony.Annotation.NetworkType;
36 import android.telephony.AnomalyReporter;
37 import android.telephony.NetworkRegistrationInfo;
38 import android.telephony.ServiceState;
39 import android.telephony.TelephonyManager;
40 import android.telephony.data.NrQosSessionAttributes;
41 import android.telephony.data.QosBearerSession;
42 import android.util.ArrayMap;
43 import android.util.LocalLog;
44 import android.util.SparseArray;
45 
46 import com.android.internal.telephony.DctConstants;
47 import com.android.internal.telephony.Phone;
48 import com.android.internal.telephony.RILConstants;
49 import com.android.internal.telephony.SlidingWindowEventCounter;
50 import com.android.internal.telephony.metrics.TelephonyMetrics;
51 import com.android.internal.util.IndentingPrintWriter;
52 import com.android.telephony.Rlog;
53 
54 import java.io.FileDescriptor;
55 import java.io.PrintWriter;
56 import java.net.InetAddress;
57 import java.time.Duration;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Objects;
61 import java.util.UUID;
62 import java.util.concurrent.Executor;
63 import java.util.concurrent.Executors;
64 import java.util.concurrent.TimeUnit;
65 
66 /**
67  * This class represents a network agent which is communication channel between
68  * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is
69  * created when data connection enters {@link DataConnection.DcActiveState} until it exits that
70  * state.
71  *
72  * Note that in IWLAN handover scenario, this agent could be transferred to the new
73  * {@link DataConnection} so for a short window of time this object might be accessed by two
74  * different {@link DataConnection}. Thus each method in this class needs to be synchronized.
75  */
76 public class DcNetworkAgent extends NetworkAgent {
77     private final String mTag;
78 
79     private final int mId;
80 
81     private final Phone mPhone;
82 
83     private int mTransportType;
84 
85     private NetworkCapabilities mNetworkCapabilities;
86 
87     public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
88 
89     private final QosCallbackTracker mQosCallbackTracker;
90 
91     private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor();
92 
93     private DataConnection mDataConnection;
94 
95     private final LocalLog mNetCapsLocalLog = new LocalLog(50);
96 
97     // For interface duplicate detection. Key is the net id, value is the interface name in string.
98     private static Map<Integer, String> sInterfaceNames = new ArrayMap<>();
99 
100     private static final long NETWORK_UNWANTED_ANOMALY_WINDOW_MS = TimeUnit.MINUTES.toMillis(5);
101     private static final int NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES =  12;
102 
DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config, NetworkProvider networkProvider, int transportType)103     DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config,
104             NetworkProvider networkProvider, int transportType) {
105         super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent",
106                 dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config,
107                 networkProvider);
108         register();
109         mId = getNetwork().getNetId();
110         mTag = "DcNetworkAgent" + "-" + mId;
111         mPhone = phone;
112         mNetworkCapabilities = dc.getNetworkCapabilities();
113         mTransportType = transportType;
114         mDataConnection = dc;
115         if (dc.getLinkProperties() != null) {
116             checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName());
117             logd("created for data connection " + dc.getName() + ", "
118                     + dc.getLinkProperties().getInterfaceName());
119         } else {
120             loge("The connection does not have a valid link properties.");
121         }
122         mQosCallbackTracker = new QosCallbackTracker(this);
123     }
124 
getNetworkType()125     private @NetworkType int getNetworkType() {
126         ServiceState ss = mPhone.getServiceState();
127         int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
128 
129         NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
130                 NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
131         if (nri != null) {
132             networkType = nri.getAccessNetworkTechnology();
133         }
134 
135         return networkType;
136     }
137 
checkDuplicateInterface(int netId, @Nullable String interfaceName)138     private void checkDuplicateInterface(int netId, @Nullable String interfaceName) {
139         for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) {
140             if (Objects.equals(interfaceName, entry.getValue())) {
141                 String message = "Duplicate interface " + interfaceName
142                         + " is detected. DcNetworkAgent-" + entry.getKey()
143                         + " already used this interface name.";
144                 loge(message);
145                 // Using fixed UUID to avoid duplicate bugreport notification
146                 AnomalyReporter.reportAnomaly(
147                         UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
148                         message);
149                 return;
150             }
151         }
152         sInterfaceNames.put(netId, interfaceName);
153     }
154 
155     /**
156      * @return The tag
157      */
getTag()158     String getTag() {
159         return mTag;
160     }
161 
162     /**
163      * Set the data connection that owns this network agent.
164      *
165      * @param dc Data connection owning this network agent.
166      * @param transportType Transport that this data connection is on.
167      */
acquireOwnership(@onNull DataConnection dc, @TransportType int transportType)168     public synchronized void acquireOwnership(@NonNull DataConnection dc,
169                                               @TransportType int transportType) {
170         mDataConnection = dc;
171         mTransportType = transportType;
172         logd(dc.getName() + " acquired the ownership of this agent.");
173     }
174 
175     /**
176      * Release the ownership of network agent.
177      */
releaseOwnership(DataConnection dc)178     public synchronized void releaseOwnership(DataConnection dc) {
179         if (mDataConnection == null) {
180             loge("releaseOwnership called on no-owner DcNetworkAgent!");
181             return;
182         } else if (mDataConnection != dc) {
183             loge("releaseOwnership: This agent belongs to "
184                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
185             return;
186         }
187         logd("Data connection " + mDataConnection.getName() + " released the ownership.");
188         mDataConnection = null;
189     }
190 
191     /**
192      * @return The data connection that owns this agent
193      */
getDataConnection()194     public synchronized DataConnection getDataConnection() {
195         return mDataConnection;
196     }
197 
198     private static final SlidingWindowEventCounter sNetworkUnwantedCounter =
199             new SlidingWindowEventCounter(NETWORK_UNWANTED_ANOMALY_WINDOW_MS,
200                     NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES);
201 
202     @Override
onNetworkUnwanted()203     public synchronized void onNetworkUnwanted() {
204         trackNetworkUnwanted();
205         if (mDataConnection == null) {
206             loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!");
207             return;
208         }
209 
210         logd("onNetworkUnwanted called. Now tear down the data connection "
211                 + mDataConnection.getName());
212         mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE,
213                 DcTracker.RELEASE_TYPE_DETACH, null);
214     }
215 
216     /**
217      * There have been several bugs where a RECONNECT loop kicks off where a DataConnection
218      * connects to the Network, ConnectivityService indicates that the Network is unwanted,
219      * and then the DataConnection reconnects.  By the time we get the bug report it's too late
220      * because there have already been hundreds of RECONNECTS.  This is meant to capture the issue
221      * when it first starts.
222      *
223      * The unwanted counter is configured to only take an anomaly report in extreme cases.
224      * This is to avoid having the anomaly message show up on several devices.
225      *
226      * This is directly related to b/175845538.  But, there have been several other occurrences of
227      * this issue.
228      */
trackNetworkUnwanted()229     private void trackNetworkUnwanted() {
230         if (sNetworkUnwantedCounter.addOccurrence()) {
231             AnomalyReporter.reportAnomaly(
232                     UUID.fromString("3f578b5c-64e9-11eb-ae93-0242ac130002"),
233                     "Network Unwanted called 12 times in 5 minutes.");
234         }
235     }
236 
237     @Override
onBandwidthUpdateRequested()238     public synchronized void onBandwidthUpdateRequested() {
239         if (mDataConnection == null) {
240             loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!");
241             return;
242         }
243 
244         if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE     // active LCE service
245                 && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
246             mPhone.mCi.pullLceData(mDataConnection.obtainMessage(
247                     DataConnection.EVENT_BW_REFRESH_RESPONSE));
248         }
249     }
250 
251     @Override
onValidationStatus(int status, Uri redirectUri)252     public synchronized void onValidationStatus(int status, Uri redirectUri) {
253         if (mDataConnection == null) {
254             loge("onValidationStatus called on no-owner DcNetworkAgent!");
255             return;
256         }
257 
258         logd("validation status: " + status + " with redirection URL: " + redirectUri);
259         DcTracker dct = mPhone.getDcTracker(mTransportType);
260         if (dct != null) {
261             Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
262                     status, mDataConnection.getCid(), redirectUri.toString());
263             msg.sendToTarget();
264         }
265     }
266 
isOwned(DataConnection dc, String reason)267     private synchronized boolean isOwned(DataConnection dc, String reason) {
268         if (mDataConnection == null) {
269             loge(reason + " called on no-owner DcNetworkAgent!");
270             return false;
271         } else if (mDataConnection != dc) {
272             loge(reason + ": This agent belongs to "
273                     + mDataConnection.getName() + ", ignored the request from " + dc.getName());
274             return false;
275         }
276         return true;
277     }
278 
279     /**
280      * Update the legacy sub type (i.e. data network type).
281      *
282      * @param dc The data connection that invokes this method.
283      */
updateLegacySubtype(DataConnection dc)284     public synchronized void updateLegacySubtype(DataConnection dc) {
285         if (!isOwned(dc, "updateLegacySubtype")) return;
286 
287         int networkType = getNetworkType();
288         setLegacySubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
289     }
290 
291     /**
292      * Set the network capabilities.
293      *
294      * @param networkCapabilities The network capabilities.
295      * @param dc The data connection that invokes this method.
296      */
sendNetworkCapabilities(NetworkCapabilities networkCapabilities, DataConnection dc)297     public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities,
298                                                      DataConnection dc) {
299         if (!isOwned(dc, "sendNetworkCapabilities")) return;
300 
301         if (!networkCapabilities.equals(mNetworkCapabilities)) {
302             String logStr = "Changed from " + mNetworkCapabilities + " to "
303                     + networkCapabilities + ", Data RAT="
304                     + mPhone.getServiceState().getRilDataRadioTechnology()
305                     + ", dc=" + mDataConnection.getName();
306             logd(logStr);
307             mNetCapsLocalLog.log(logStr);
308             if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
309                 // only log metrics for DataConnection with NET_CAPABILITY_INTERNET
310                 if (mNetworkCapabilities == null
311                         || networkCapabilities.hasCapability(
312                                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
313                         != mNetworkCapabilities.hasCapability(
314                                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) {
315                     TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent(
316                             mPhone.getPhoneId(), networkCapabilities);
317                 }
318             }
319             mNetworkCapabilities = networkCapabilities;
320         }
321         sendNetworkCapabilities(networkCapabilities);
322     }
323 
324     /**
325      * Set the link properties
326      *
327      * @param linkProperties The link properties
328      * @param dc The data connection that invokes this method.
329      */
sendLinkProperties(@onNull LinkProperties linkProperties, DataConnection dc)330     public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties,
331                                                 DataConnection dc) {
332         if (!isOwned(dc, "sendLinkProperties")) return;
333 
334         sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName());
335         sendLinkProperties(linkProperties);
336     }
337 
338     /**
339      * Set the network score.
340      *
341      * @param score The network score.
342      * @param dc The data connection that invokes this method.
343      */
sendNetworkScore(int score, DataConnection dc)344     public synchronized void sendNetworkScore(int score, DataConnection dc) {
345         if (!isOwned(dc, "sendNetworkScore")) return;
346         sendNetworkScore(score);
347     }
348 
349     /**
350      * Unregister the network agent from connectivity service.
351      *
352      * @param dc The data connection that invokes this method.
353      */
unregister(DataConnection dc)354     public synchronized void unregister(DataConnection dc) {
355         if (!isOwned(dc, "unregister")) return;
356 
357         logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed.");
358         sInterfaceNames.remove(mId);
359         super.unregister();
360     }
361 
362     @Override
onStartSocketKeepalive(int slot, @NonNull Duration interval, @NonNull KeepalivePacketData packet)363     public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval,
364             @NonNull KeepalivePacketData packet) {
365         if (mDataConnection == null) {
366             loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!");
367             return;
368         }
369 
370         if (packet instanceof NattKeepalivePacketData) {
371             mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST,
372                     slot, (int) interval.getSeconds(), packet).sendToTarget();
373         } else {
374             sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED);
375         }
376     }
377 
378     @Override
onStopSocketKeepalive(int slot)379     public synchronized void onStopSocketKeepalive(int slot) {
380         if (mDataConnection == null) {
381             loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!");
382             return;
383         }
384 
385         mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot)
386                 .sendToTarget();
387     }
388 
389     @Override
onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter)390     public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
391         mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId,
392               new QosCallbackTracker.IFilter() {
393                   @Override
394                   public boolean matchesLocalAddress(
395                           InetAddress address, int startPort, int endPort) {
396                       return filter.matchesLocalAddress(address, startPort, endPort);
397                   }
398 
399                   @Override
400                   public boolean matchesRemoteAddress(
401                           InetAddress address, int startPort, int endPort) {
402                       return filter.matchesRemoteAddress(address, startPort, endPort);
403                   }
404               }));
405     }
406 
407     @Override
onQosCallbackUnregistered(final int qosCallbackId)408     public void onQosCallbackUnregistered(final int qosCallbackId) {
409         mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId));
410     }
411 
updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions)412     void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) {
413         mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions));
414     }
415 
notifyQosSessionAvailable(final int qosCallbackId, final int sessionId, @NonNull final QosSessionAttributes attributes)416     public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
417             @NonNull final QosSessionAttributes attributes) {
418         super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
419     }
420 
notifyQosSessionLost(final int qosCallbackId, final int sessionId, final int qosSessionType)421     public void notifyQosSessionLost(final int qosCallbackId,
422             final int sessionId, final int qosSessionType) {
423         super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
424     }
425 
426     @Override
toString()427     public String toString() {
428         return "DcNetworkAgent-"
429                 + mId
430                 + " mDataConnection="
431                 + ((mDataConnection != null) ? mDataConnection.getName() : null)
432                 + " mTransportType="
433                 + AccessNetworkConstants.transportTypeToString(mTransportType)
434                 + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null)
435                 + " mNetworkCapabilities=" + mNetworkCapabilities;
436     }
437 
438     /**
439      * Dump the state of transport manager
440      *
441      * @param fd File descriptor
442      * @param printWriter Print writer
443      * @param args Arguments
444      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)445     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
446         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
447         pw.println(toString());
448         pw.increaseIndent();
449         pw.println("Net caps logs:");
450         mNetCapsLocalLog.dump(fd, pw, args);
451         pw.decreaseIndent();
452     }
453 
454     /**
455      * Log with debug level
456      *
457      * @param s is string log
458      */
logd(String s)459     private void logd(String s) {
460         Rlog.d(mTag, s);
461     }
462 
463     /**
464      * Log with error level
465      *
466      * @param s is string log
467      */
loge(String s)468     private void loge(String s) {
469         Rlog.e(mTag, s);
470     }
471 
472     class DcKeepaliveTracker {
473         private class KeepaliveRecord {
474             public int slotId;
475             public int currentStatus;
476 
KeepaliveRecord(int slotId, int status)477             KeepaliveRecord(int slotId, int status) {
478                 this.slotId = slotId;
479                 this.currentStatus = status;
480             }
481         }
482 
483         private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray();
484 
getHandleForSlot(int slotId)485         int getHandleForSlot(int slotId) {
486             for (int i = 0; i < mKeepalives.size(); i++) {
487                 KeepaliveRecord kr = mKeepalives.valueAt(i);
488                 if (kr.slotId == slotId) return mKeepalives.keyAt(i);
489             }
490             return -1;
491         }
492 
keepaliveStatusErrorToPacketKeepaliveError(int error)493         int keepaliveStatusErrorToPacketKeepaliveError(int error) {
494             switch(error) {
495                 case KeepaliveStatus.ERROR_NONE:
496                     return SocketKeepalive.SUCCESS;
497                 case KeepaliveStatus.ERROR_UNSUPPORTED:
498                     return SocketKeepalive.ERROR_UNSUPPORTED;
499                 case KeepaliveStatus.ERROR_NO_RESOURCES:
500                     return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
501                 case KeepaliveStatus.ERROR_UNKNOWN:
502                 default:
503                     return SocketKeepalive.ERROR_HARDWARE_ERROR;
504             }
505         }
506 
handleKeepaliveStarted(final int slot, KeepaliveStatus ks)507         void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
508             switch (ks.statusCode) {
509                 case KeepaliveStatus.STATUS_INACTIVE:
510                     DcNetworkAgent.this.sendSocketKeepaliveEvent(slot,
511                             keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
512                     break;
513                 case KeepaliveStatus.STATUS_ACTIVE:
514                     DcNetworkAgent.this.sendSocketKeepaliveEvent(
515                             slot, SocketKeepalive.SUCCESS);
516                     // fall through to add record
517                 case KeepaliveStatus.STATUS_PENDING:
518                     logd("Adding keepalive handle="
519                             + ks.sessionHandle + " slot = " + slot);
520                     mKeepalives.put(ks.sessionHandle,
521                             new KeepaliveRecord(
522                                     slot, ks.statusCode));
523                     break;
524                 default:
525                     logd("Invalid KeepaliveStatus Code: " + ks.statusCode);
526                     break;
527             }
528         }
529 
handleKeepaliveStatus(KeepaliveStatus ks)530         void handleKeepaliveStatus(KeepaliveStatus ks) {
531             final KeepaliveRecord kr;
532             kr = mKeepalives.get(ks.sessionHandle);
533 
534             if (kr == null) {
535                 // If there is no slot for the session handle, we received an event
536                 // for a different data connection. This is not an error because the
537                 // keepalive session events are broadcast to all listeners.
538                 loge("Discarding keepalive event for different data connection:" + ks);
539                 return;
540             }
541             // Switch on the current state, to see what we do with the status update
542             switch (kr.currentStatus) {
543                 case KeepaliveStatus.STATUS_INACTIVE:
544                     logd("Inactive Keepalive received status!");
545                     DcNetworkAgent.this.sendSocketKeepaliveEvent(
546                             kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR);
547                     break;
548                 case KeepaliveStatus.STATUS_PENDING:
549                     switch (ks.statusCode) {
550                         case KeepaliveStatus.STATUS_INACTIVE:
551                             DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId,
552                                     keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
553                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
554                             mKeepalives.remove(ks.sessionHandle);
555                             break;
556                         case KeepaliveStatus.STATUS_ACTIVE:
557                             logd("Pending Keepalive received active status!");
558                             kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
559                             DcNetworkAgent.this.sendSocketKeepaliveEvent(
560                                     kr.slotId, SocketKeepalive.SUCCESS);
561                             break;
562                         case KeepaliveStatus.STATUS_PENDING:
563                             loge("Invalid unsolicied Keepalive Pending Status!");
564                             break;
565                         default:
566                             loge("Invalid Keepalive Status received, " + ks.statusCode);
567                     }
568                     break;
569                 case KeepaliveStatus.STATUS_ACTIVE:
570                     switch (ks.statusCode) {
571                         case KeepaliveStatus.STATUS_INACTIVE:
572                             logd("Keepalive received stopped status!");
573                             DcNetworkAgent.this.sendSocketKeepaliveEvent(
574                                     kr.slotId, SocketKeepalive.SUCCESS);
575 
576                             kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
577                             mKeepalives.remove(ks.sessionHandle);
578                             break;
579                         case KeepaliveStatus.STATUS_PENDING:
580                         case KeepaliveStatus.STATUS_ACTIVE:
581                             loge("Active Keepalive received invalid status!");
582                             break;
583                         default:
584                             loge("Invalid Keepalive Status received, " + ks.statusCode);
585                     }
586                     break;
587                 default:
588                     loge("Invalid Keepalive Status received, " + kr.currentStatus);
589             }
590         }
591     }
592 }
593