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