/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.googlecode.android_scripting.facade.net; import android.app.Service; import android.content.Context; import android.net.ConnectivityManager; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.Network; import android.net.NetworkCapabilities; import android.net.SocketKeepalive; import android.net.util.KeepaliveUtils; import com.googlecode.android_scripting.Log; import com.googlecode.android_scripting.facade.ConnectivityConstants; import com.googlecode.android_scripting.facade.ConnectivityEvents.SocketKeepaliveEvent; import com.googlecode.android_scripting.facade.EventFacade; import com.googlecode.android_scripting.facade.FacadeManager; import com.googlecode.android_scripting.jsonrpc.RpcReceiver; import com.googlecode.android_scripting.rpc.Rpc; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Socket keepalive SL4A APIs */ public class SocketKeepaliveFacade extends RpcReceiver { private enum Event { Invalid(0), Started(1 << 0), Stopped(1 << 1), Error(1 << 2), OnDataReceived(1 << 3), EventAll(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3); private int mType; Event(int type) { mType = type; } private int getType() { return mType; } } class SocketKeepaliveReceiver extends SocketKeepalive.Callback { private int mEvents; public String mId; public SocketKeepalive mSocketKeepalive; SocketKeepaliveReceiver(int events) { super(); mEvents = events; mId = this.toString(); } public void startListeningForEvents(int events) { mEvents |= events & Event.EventAll.getType(); } public void stopListeningForEvents(int events) { mEvents &= ~(events & Event.EventAll.getType()); } private void handleEvent(Event e) { Log.d("SocketKeepaliveFacade: SocketKeepaliveCallback - " + e.toString()); if ((mEvents & e.getType()) == e.getType()) { mEventFacade.postEvent( ConnectivityConstants.EventSocketKeepaliveCallback, new SocketKeepaliveEvent(mId, e.toString())); } } @Override public void onStarted() { handleEvent(Event.Started); } @Override public void onStopped() { handleEvent(Event.Stopped); } @Override public void onError(int error) { handleEvent(Event.Error); } @Override public void onDataReceived() { handleEvent(Event.OnDataReceived); } } private final ConnectivityManager mManager; private final Service mService; private final Context mContext; private final EventFacade mEventFacade; private static HashMap sSocketKeepaliveReceiverMap = new HashMap(); private final Executor mExecutor = Executors.newSingleThreadExecutor(); public SocketKeepaliveFacade(FacadeManager manager) { super(manager); mService = manager.getService(); mContext = mService.getBaseContext(); mManager = (ConnectivityManager) mService.getSystemService(Context.CONNECTIVITY_SERVICE); mEventFacade = manager.getReceiver(EventFacade.class); } /** * Start NATT socket keepalive * @param udpEncapsulationSocketId : hash key of {@link UdpEncapsulationSocket} * @param srcAddrString : source addr in string * @param dstAddrString : destination addr in string * @param intervalSeconds : keepalive interval in seconds * @return Keepalive key if successful, null if not */ @Rpc(description = "start natt socket keepalive") public String startNattSocketKeepalive( String udpEncapsulationSocketId, String srcAddrString, String dstAddrString, Integer intervalSeconds) { try { UdpEncapsulationSocket udpEncapSocket = IpSecManagerFacade.getUdpEncapsulationSocket(udpEncapsulationSocketId); Network network = mManager.getActiveNetwork(); InetAddress srcAddr = InetAddress.getByName(srcAddrString); InetAddress dstAddr = InetAddress.getByName(dstAddrString); Log.d("SocketKeepaliveFacade: startNattKeepalive intervalSeconds:" + intervalSeconds); SocketKeepaliveReceiver socketKeepaliveReceiver = new SocketKeepaliveReceiver( Event.EventAll.getType()); SocketKeepalive socketKeepalive = mManager.createSocketKeepalive( network, udpEncapSocket, srcAddr, dstAddr, mExecutor, socketKeepaliveReceiver); socketKeepalive.start(intervalSeconds); if (socketKeepalive != null) { socketKeepaliveReceiver.mSocketKeepalive = socketKeepalive; String key = socketKeepaliveReceiver.mId; sSocketKeepaliveReceiverMap.put(key, socketKeepaliveReceiver); return key; } else { Log.e("SocketKeepaliveFacade: Failed to start Natt socketkeepalive"); return null; } } catch (UnknownHostException e) { Log.e("SocketKeepaliveFacade: SocketNattKeepalive exception", e); return null; } } /** * Start TCP socket keepalive * @param socketId : Hash key of socket object * @param intervalSeconds : keepalive interval in seconds * @return Keepalive key if successful, null if not */ @Rpc(description = "Start TCP socket keepalive") public String startTcpSocketKeepalive(String socketId, Integer intervalSeconds) { Socket socket = SocketFacade.getSocket(socketId); Network network = mManager.getActiveNetwork(); Log.d("SocketKeepaliveFacade: Keepalive interval seconds: " + intervalSeconds); SocketKeepaliveReceiver socketKeepaliveReceiver = new SocketKeepaliveReceiver( Event.EventAll.getType()); SocketKeepalive socketKeepalive = mManager.createSocketKeepalive( network, socket, mExecutor, socketKeepaliveReceiver); socketKeepalive.start(intervalSeconds); if (socketKeepalive == null) { Log.e("SocketKeepaliveFacade: Failed to start TCP SocketKeepalive"); return null; } socketKeepaliveReceiver.mSocketKeepalive = socketKeepalive; String key = socketKeepaliveReceiver.mId; sSocketKeepaliveReceiverMap.put(key, socketKeepaliveReceiver); return key; } /** * Stop socket keepalive * @param key : {@link SocketKeepaliveReceiver} * @return true if stopping keepalive is successful, false if not */ @Rpc(description = "stop socket keepalive") public Boolean stopSocketKeepalive(String key) { SocketKeepaliveReceiver mSocketKeepaliveReceiver = sSocketKeepaliveReceiverMap.get(key); if (mSocketKeepaliveReceiver == null) { return false; } mSocketKeepaliveReceiver.mSocketKeepalive.stop(); return true; } /** * Remove key from the SocketKeepaliveReceiver map * @param key : Hash key of the keepalive object */ @Rpc(description = "remove SocketKeepaliveReceiver key") public void removeSocketKeepaliveReceiverKey(String key) { sSocketKeepaliveReceiverMap.remove(key); } /** * Start listening for a socket keepalive event * @param key : hash key of {@link SocketKeepaliveReceiver} * @param eventString : type of keepalive event - Started, Stopped, Error * @return True if listening for event successful, false if not */ @Rpc(description = "start listening for SocketKeepalive Event") public Boolean socketKeepaliveStartListeningForEvent(String key, String eventString) { SocketKeepaliveReceiver mSocketKeepaliveReceiver = sSocketKeepaliveReceiverMap.get(key); int event = Event.valueOf(eventString).getType(); if (mSocketKeepaliveReceiver == null || event == Event.Invalid.getType()) { return false; } mSocketKeepaliveReceiver.startListeningForEvents(event); return true; } /** * Stop listening for a for socket keepalive event * @param key : hash key of {@link SocketKeepaliveReceiver} * @param eventString : type of keepalive event - Started, Stopped, Error * @return True if listening event successful, false if not */ @Rpc(description = "stop listening for SocketKeepalive Event") public Boolean socketKeepaliveStopListeningForEvent(String key, String eventString) { SocketKeepaliveReceiver mSocketKeepaliveReceiver = sSocketKeepaliveReceiverMap.get(key); int event = Event.valueOf(eventString).getType(); if (mSocketKeepaliveReceiver == null || event == Event.Invalid.getType()) { return false; } mSocketKeepaliveReceiver.stopListeningForEvents(event); return true; } /** * Get maximum supported keepalives for active network * @return Max number of keepalives supported in int */ @Rpc(description = "get max number of keepalives supported for active network") public int getSupportedKeepalivesForNetwork() { NetworkCapabilities netCap = mManager.getNetworkCapabilities(mManager.getActiveNetwork()); int[] ka = KeepaliveUtils.getSupportedKeepalives(mContext); return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(ka, netCap); } @Override public void shutdown() {} }