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