1 /*
2  * Copyright (C) 2014 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 android.net;
18 
19 import android.annotation.NonNull;
20 import android.annotation.RequiresPermission;
21 import android.annotation.SystemApi;
22 import android.annotation.SystemService;
23 import android.annotation.TestApi;
24 import android.compat.annotation.UnsupportedAppUsage;
25 import android.content.Context;
26 import android.os.Build;
27 import android.os.RemoteException;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.os.BackgroundThread;
31 
32 import java.util.ArrayList;
33 import java.util.Objects;
34 import java.util.concurrent.Executor;
35 
36 /**
37  * A class representing the IP configuration of the Ethernet network.
38  *
39  * @hide
40  */
41 @SystemApi
42 @SystemService(Context.ETHERNET_SERVICE)
43 public class EthernetManager {
44     private static final String TAG = "EthernetManager";
45 
46     private final IEthernetManager mService;
47     @GuardedBy("mListeners")
48     private final ArrayList<ListenerInfo> mListeners = new ArrayList<>();
49     private final IEthernetServiceListener.Stub mServiceListener =
50             new IEthernetServiceListener.Stub() {
51                 @Override
52                 public void onAvailabilityChanged(String iface, boolean isAvailable) {
53                     synchronized (mListeners) {
54                         for (ListenerInfo li : mListeners) {
55                             li.executor.execute(() ->
56                                     li.listener.onAvailabilityChanged(iface, isAvailable));
57                         }
58                     }
59                 }
60             };
61 
62     private static class ListenerInfo {
63         @NonNull
64         public final Executor executor;
65         @NonNull
66         public final Listener listener;
67 
ListenerInfo(@onNull Executor executor, @NonNull Listener listener)68         private ListenerInfo(@NonNull Executor executor, @NonNull Listener listener) {
69             this.executor = executor;
70             this.listener = listener;
71         }
72     }
73 
74     /**
75      * A listener interface to receive notification on changes in Ethernet.
76      * @hide
77      */
78     public interface Listener {
79         /**
80          * Called when Ethernet port's availability is changed.
81          * @param iface Ethernet interface name
82          * @param isAvailable {@code true} if Ethernet port exists.
83          * @hide
84          */
85         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onAvailabilityChanged(String iface, boolean isAvailable)86         void onAvailabilityChanged(String iface, boolean isAvailable);
87     }
88 
89     /**
90      * Create a new EthernetManager instance.
91      * Applications will almost always want to use
92      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
93      * the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}.
94      * @hide
95      */
EthernetManager(Context context, IEthernetManager service)96     public EthernetManager(Context context, IEthernetManager service) {
97         mService = service;
98     }
99 
100     /**
101      * Get Ethernet configuration.
102      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
103      * @hide
104      */
105     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getConfiguration(String iface)106     public IpConfiguration getConfiguration(String iface) {
107         try {
108             return mService.getConfiguration(iface);
109         } catch (RemoteException e) {
110             throw e.rethrowFromSystemServer();
111         }
112     }
113 
114     /**
115      * Set Ethernet configuration.
116      * @hide
117      */
118     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setConfiguration(String iface, IpConfiguration config)119     public void setConfiguration(String iface, IpConfiguration config) {
120         try {
121             mService.setConfiguration(iface, config);
122         } catch (RemoteException e) {
123             throw e.rethrowFromSystemServer();
124         }
125     }
126 
127     /**
128      * Indicates whether the system currently has one or more Ethernet interfaces.
129      * @hide
130      */
131     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isAvailable()132     public boolean isAvailable() {
133         return getAvailableInterfaces().length > 0;
134     }
135 
136     /**
137      * Indicates whether the system has given interface.
138      *
139      * @param iface Ethernet interface name
140      * @hide
141      */
142     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isAvailable(String iface)143     public boolean isAvailable(String iface) {
144         try {
145             return mService.isAvailable(iface);
146         } catch (RemoteException e) {
147             throw e.rethrowFromSystemServer();
148         }
149     }
150 
151     /**
152      * Adds a listener.
153      *
154      * Consider using {@link #addListener(Listener, Executor)} instead: this method uses a default
155      * executor that may have higher latency than a provided executor.
156      * @param listener A {@link Listener} to add.
157      * @throws IllegalArgumentException If the listener is null.
158      * @hide
159      */
160     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
addListener(@onNull Listener listener)161     public void addListener(@NonNull Listener listener) {
162         addListener(listener, BackgroundThread.getExecutor());
163     }
164 
165     /**
166      * Adds a listener.
167      * @param listener A {@link Listener} to add.
168      * @param executor Executor to run callbacks on.
169      * @throws IllegalArgumentException If the listener or executor is null.
170      * @hide
171      */
172     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
addListener(@onNull Listener listener, @NonNull Executor executor)173     public void addListener(@NonNull Listener listener, @NonNull Executor executor) {
174         if (listener == null || executor == null) {
175             throw new NullPointerException("listener and executor must not be null");
176         }
177         synchronized (mListeners) {
178             mListeners.add(new ListenerInfo(executor, listener));
179             if (mListeners.size() == 1) {
180                 try {
181                     mService.addListener(mServiceListener);
182                 } catch (RemoteException e) {
183                     throw e.rethrowFromSystemServer();
184                 }
185             }
186         }
187     }
188 
189     /**
190      * Returns an array of available Ethernet interface names.
191      * @hide
192      */
193     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getAvailableInterfaces()194     public String[] getAvailableInterfaces() {
195         try {
196             return mService.getAvailableInterfaces();
197         } catch (RemoteException e) {
198             throw e.rethrowAsRuntimeException();
199         }
200     }
201 
202     /**
203      * Removes a listener.
204      * @param listener A {@link Listener} to remove.
205      * @throws IllegalArgumentException If the listener is null.
206      * @hide
207      */
208     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
removeListener(@onNull Listener listener)209     public void removeListener(@NonNull Listener listener) {
210         if (listener == null) {
211             throw new IllegalArgumentException("listener must not be null");
212         }
213         synchronized (mListeners) {
214             mListeners.removeIf(l -> l.listener == listener);
215             if (mListeners.isEmpty()) {
216                 try {
217                     mService.removeListener(mServiceListener);
218                 } catch (RemoteException e) {
219                     throw e.rethrowFromSystemServer();
220                 }
221             }
222         }
223     }
224 
225     /**
226      * Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface}
227      * as Ethernet interfaces. The effects of this method apply to any test interfaces that are
228      * already present on the system.
229      * @hide
230      */
231     @TestApi
setIncludeTestInterfaces(boolean include)232     public void setIncludeTestInterfaces(boolean include) {
233         try {
234             mService.setIncludeTestInterfaces(include);
235         } catch (RemoteException e) {
236             throw e.rethrowFromSystemServer();
237         }
238     }
239 
240     /**
241      * A request for a tethered interface.
242      */
243     public static class TetheredInterfaceRequest {
244         private final IEthernetManager mService;
245         private final ITetheredInterfaceCallback mCb;
246 
TetheredInterfaceRequest(@onNull IEthernetManager service, @NonNull ITetheredInterfaceCallback cb)247         private TetheredInterfaceRequest(@NonNull IEthernetManager service,
248                 @NonNull ITetheredInterfaceCallback cb) {
249             this.mService = service;
250             this.mCb = cb;
251         }
252 
253         /**
254          * Release the request, causing the interface to revert back from tethering mode if there
255          * is no other requestor.
256          */
release()257         public void release() {
258             try {
259                 mService.releaseTetheredInterface(mCb);
260             } catch (RemoteException e) {
261                 e.rethrowFromSystemServer();
262             }
263         }
264     }
265 
266     /**
267      * Callback for {@link #requestTetheredInterface(TetheredInterfaceCallback)}.
268      */
269     public interface TetheredInterfaceCallback {
270         /**
271          * Called when the tethered interface is available.
272          * @param iface The name of the interface.
273          */
onAvailable(@onNull String iface)274         void onAvailable(@NonNull String iface);
275 
276         /**
277          * Called when the tethered interface is now unavailable.
278          */
onUnavailable()279         void onUnavailable();
280     }
281 
282     /**
283      * Request a tethered interface in tethering mode.
284      *
285      * <p>When this method is called and there is at least one ethernet interface available, the
286      * system will designate one to act as a tethered interface. If there is already a tethered
287      * interface, the existing interface will be used.
288      * @param callback A callback to be called once the request has been fulfilled.
289      */
290     @RequiresPermission(anyOf = {
291             android.Manifest.permission.NETWORK_STACK,
292             android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
293     })
294     @NonNull
requestTetheredInterface(@onNull final Executor executor, @NonNull final TetheredInterfaceCallback callback)295     public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor,
296             @NonNull final TetheredInterfaceCallback callback) {
297         Objects.requireNonNull(callback, "Callback must be non-null");
298         Objects.requireNonNull(executor, "Executor must be non-null");
299         final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
300             @Override
301             public void onAvailable(String iface) {
302                 executor.execute(() -> callback.onAvailable(iface));
303             }
304 
305             @Override
306             public void onUnavailable() {
307                 executor.execute(() -> callback.onUnavailable());
308             }
309         };
310 
311         try {
312             mService.requestTetheredInterface(cbInternal);
313         } catch (RemoteException e) {
314             throw e.rethrowFromSystemServer();
315         }
316         return new TetheredInterfaceRequest(mService, cbInternal);
317     }
318 }
319