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