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 package com.android.server; 17 18 import static android.net.RouteInfo.RTN_UNICAST; 19 20 import android.net.INetd; 21 import android.net.INetdUnsolicitedEventListener; 22 import android.net.InetAddresses; 23 import android.net.IpPrefix; 24 import android.net.LinkAddress; 25 import android.net.RouteInfo; 26 import android.os.Handler; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import androidx.annotation.NonNull; 31 32 import java.util.Map; 33 import java.util.Optional; 34 import java.util.concurrent.ConcurrentHashMap; 35 36 /** 37 * A class for reporting network events to clients. 38 * 39 * Implements INetdUnsolicitedEventListener and registers with netd, and relays those events to 40 * all INetworkManagementEventObserver objects that have registered with it. 41 */ 42 public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub { 43 private static final String TAG = NetworkObserverRegistry.class.getSimpleName(); 44 45 /** 46 * Start listening for Netd events. 47 * 48 * <p>This should be called before allowing any observer to be registered. 49 * Note there is no unregister method. The only way to unregister is when the process 50 * terminates. 51 */ register(@onNull INetd netd)52 public void register(@NonNull INetd netd) throws RemoteException { 53 netd.registerUnsolicitedEventListener(this); 54 } 55 56 private final ConcurrentHashMap<NetworkObserver, Optional<Handler>> mObservers = 57 new ConcurrentHashMap<>(); 58 59 /** 60 * Registers the specified observer and start sending callbacks to it. 61 * This method may be called on any thread. 62 */ registerObserver(@onNull NetworkObserver observer, @NonNull Handler handler)63 public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) { 64 if (handler == null) { 65 throw new IllegalArgumentException("handler must be non-null"); 66 } 67 mObservers.put(observer, Optional.of(handler)); 68 } 69 70 /** 71 * Registers the specified observer, and start sending callbacks to it. 72 * 73 * <p>This method must only be called with callbacks that are nonblocking, such as callbacks 74 * that only send a message to a StateMachine. 75 */ registerObserverForNonblockingCallback(@onNull NetworkObserver observer)76 public void registerObserverForNonblockingCallback(@NonNull NetworkObserver observer) { 77 mObservers.put(observer, Optional.empty()); 78 } 79 80 /** 81 * Unregisters the specified observer and stop sending callbacks to it. 82 * This method may be called on any thread. 83 */ unregisterObserver(@onNull NetworkObserver observer)84 public void unregisterObserver(@NonNull NetworkObserver observer) { 85 mObservers.remove(observer); 86 } 87 88 @FunctionalInterface 89 private interface NetworkObserverEventCallback { sendCallback(NetworkObserver o)90 void sendCallback(NetworkObserver o); 91 } 92 invokeForAllObservers(@onNull final NetworkObserverEventCallback callback)93 private void invokeForAllObservers(@NonNull final NetworkObserverEventCallback callback) { 94 // ConcurrentHashMap#entrySet is weakly consistent: observers that were in the map before 95 // creation will be processed, those added during traversal may or may not. 96 for (Map.Entry<NetworkObserver, Optional<Handler>> entry : mObservers.entrySet()) { 97 final NetworkObserver observer = entry.getKey(); 98 final Optional<Handler> handler = entry.getValue(); 99 if (handler.isPresent()) { 100 handler.get().post(() -> callback.sendCallback(observer)); 101 return; 102 } 103 104 try { 105 callback.sendCallback(observer); 106 } catch (RuntimeException e) { 107 Log.e(TAG, "Error sending callback to observer", e); 108 } 109 } 110 } 111 112 @Override onInterfaceClassActivityChanged(boolean isActive, int label, long timestamp, int uid)113 public void onInterfaceClassActivityChanged(boolean isActive, 114 int label, long timestamp, int uid) { 115 invokeForAllObservers(o -> o.onInterfaceClassActivityChanged( 116 isActive, label, timestamp, uid)); 117 } 118 119 /** 120 * Notify our observers of a limit reached. 121 */ 122 @Override onQuotaLimitReached(String alertName, String ifName)123 public void onQuotaLimitReached(String alertName, String ifName) { 124 invokeForAllObservers(o -> o.onQuotaLimitReached(alertName, ifName)); 125 } 126 127 @Override onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers)128 public void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) { 129 invokeForAllObservers(o -> o.onInterfaceDnsServerInfo(ifName, lifetime, servers)); 130 } 131 132 @Override onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope)133 public void onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope) { 134 final LinkAddress address = new LinkAddress(addr, flags, scope); 135 invokeForAllObservers(o -> o.onInterfaceAddressUpdated(address, ifName)); 136 } 137 138 @Override onInterfaceAddressRemoved(String addr, String ifName, int flags, int scope)139 public void onInterfaceAddressRemoved(String addr, 140 String ifName, int flags, int scope) { 141 final LinkAddress address = new LinkAddress(addr, flags, scope); 142 invokeForAllObservers(o -> o.onInterfaceAddressRemoved(address, ifName)); 143 } 144 145 @Override onInterfaceAdded(String ifName)146 public void onInterfaceAdded(String ifName) { 147 invokeForAllObservers(o -> o.onInterfaceAdded(ifName)); 148 } 149 150 @Override onInterfaceRemoved(String ifName)151 public void onInterfaceRemoved(String ifName) { 152 invokeForAllObservers(o -> o.onInterfaceRemoved(ifName)); 153 } 154 155 @Override onInterfaceChanged(String ifName, boolean up)156 public void onInterfaceChanged(String ifName, boolean up) { 157 invokeForAllObservers(o -> o.onInterfaceChanged(ifName, up)); 158 } 159 160 @Override onInterfaceLinkStateChanged(String ifName, boolean up)161 public void onInterfaceLinkStateChanged(String ifName, boolean up) { 162 invokeForAllObservers(o -> o.onInterfaceLinkStateChanged(ifName, up)); 163 } 164 165 @Override onRouteChanged(boolean updated, String route, String gateway, String ifName)166 public void onRouteChanged(boolean updated, String route, String gateway, String ifName) { 167 final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), 168 ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), 169 ifName, RTN_UNICAST); 170 if (updated) { 171 invokeForAllObservers(o -> o.onRouteUpdated(processRoute)); 172 } else { 173 invokeForAllObservers(o -> o.onRouteRemoved(processRoute)); 174 } 175 } 176 177 @Override onStrictCleartextDetected(int uid, String hex)178 public void onStrictCleartextDetected(int uid, String hex) {} 179 180 @Override getInterfaceVersion()181 public int getInterfaceVersion() { 182 return INetdUnsolicitedEventListener.VERSION; 183 } 184 185 @Override getInterfaceHash()186 public String getInterfaceHash() { 187 return INetdUnsolicitedEventListener.HASH; 188 } 189 } 190