Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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 com.android.server; |
| 18 | |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 19 | import android.annotation.NonNull; |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 20 | import android.annotation.Nullable; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 21 | import android.content.Context; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 22 | import android.net.ConnectivityManager; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 23 | import android.net.INetd; |
| 24 | import android.net.ITestNetworkManager; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 25 | import android.net.IpPrefix; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 26 | import android.net.LinkAddress; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 27 | import android.net.LinkProperties; |
| 28 | import android.net.NetworkAgent; |
| 29 | import android.net.NetworkCapabilities; |
| 30 | import android.net.NetworkInfo; |
| 31 | import android.net.NetworkInfo.DetailedState; |
| 32 | import android.net.RouteInfo; |
| 33 | import android.net.StringNetworkSpecifier; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 34 | import android.net.TestNetworkInterface; |
| 35 | import android.net.util.NetdService; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 36 | import android.os.Binder; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 37 | import android.os.Handler; |
| 38 | import android.os.HandlerThread; |
| 39 | import android.os.IBinder; |
| 40 | import android.os.INetworkManagementService; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 41 | import android.os.Looper; |
| 42 | import android.os.ParcelFileDescriptor; |
| 43 | import android.os.RemoteException; |
| 44 | import android.util.SparseArray; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 45 | |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 46 | import com.android.internal.annotations.GuardedBy; |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 47 | import com.android.internal.annotations.VisibleForTesting; |
| 48 | |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 49 | import java.io.UncheckedIOException; |
| 50 | import java.net.Inet4Address; |
| 51 | import java.net.Inet6Address; |
| 52 | import java.net.InterfaceAddress; |
| 53 | import java.net.NetworkInterface; |
| 54 | import java.net.SocketException; |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 55 | import java.util.ArrayList; |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 56 | import java.util.Objects; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 57 | import java.util.concurrent.atomic.AtomicInteger; |
| 58 | |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 59 | /** @hide */ |
| 60 | class TestNetworkService extends ITestNetworkManager.Stub { |
| 61 | @NonNull private static final String TAG = TestNetworkService.class.getSimpleName(); |
| 62 | @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK"; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 63 | @NonNull private static final String TEST_TUN_PREFIX = "testtun"; |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 64 | @NonNull private static final String TEST_TAP_PREFIX = "testtap"; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 65 | @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger(); |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 66 | |
| 67 | @NonNull private final Context mContext; |
| 68 | @NonNull private final INetworkManagementService mNMS; |
| 69 | @NonNull private final INetd mNetd; |
| 70 | |
| 71 | @NonNull private final HandlerThread mHandlerThread; |
| 72 | @NonNull private final Handler mHandler; |
| 73 | |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 74 | // Native method stubs |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 75 | private static native int jniCreateTunTap(boolean isTun, @NonNull String iface); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 76 | |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 77 | @VisibleForTesting |
| 78 | protected TestNetworkService( |
| 79 | @NonNull Context context, @NonNull INetworkManagementService netManager) { |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 80 | mHandlerThread = new HandlerThread("TestNetworkServiceThread"); |
| 81 | mHandlerThread.start(); |
| 82 | mHandler = new Handler(mHandlerThread.getLooper()); |
| 83 | |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 84 | mContext = Objects.requireNonNull(context, "missing Context"); |
| 85 | mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); |
| 86 | mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance"); |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | /** |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 90 | * Create a TUN or TAP interface with the given interface name and link addresses |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 91 | * |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 92 | * <p>This method will return the FileDescriptor to the interface. Close it to tear down the |
| 93 | * interface. |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 94 | */ |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 95 | private TestNetworkInterface createInterface(boolean isTun, LinkAddress[] linkAddrs) { |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 96 | enforceTestNetworkPermissions(mContext); |
| 97 | |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 98 | Objects.requireNonNull(linkAddrs, "missing linkAddrs"); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 99 | |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 100 | String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX; |
| 101 | String iface = ifacePrefix + sTestTunIndex.getAndIncrement(); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 102 | return Binder.withCleanCallingIdentity( |
| 103 | () -> { |
| 104 | try { |
| 105 | ParcelFileDescriptor tunIntf = |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 106 | ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface)); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 107 | for (LinkAddress addr : linkAddrs) { |
| 108 | mNetd.interfaceAddAddress( |
| 109 | iface, |
| 110 | addr.getAddress().getHostAddress(), |
| 111 | addr.getPrefixLength()); |
| 112 | } |
| 113 | |
| 114 | return new TestNetworkInterface(tunIntf, iface); |
| 115 | } catch (RemoteException e) { |
| 116 | throw e.rethrowFromSystemServer(); |
| 117 | } |
| 118 | }); |
| 119 | } |
| 120 | |
Lorenzo Colitti | b15fcce | 2019-04-01 23:41:12 +0900 | [diff] [blame] | 121 | /** |
| 122 | * Create a TUN interface with the given interface name and link addresses |
| 123 | * |
| 124 | * <p>This method will return the FileDescriptor to the TUN interface. Close it to tear down the |
| 125 | * TUN interface. |
| 126 | */ |
| 127 | @Override |
| 128 | public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) { |
| 129 | return createInterface(true, linkAddrs); |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Create a TAP interface with the given interface name |
| 134 | * |
| 135 | * <p>This method will return the FileDescriptor to the TAP interface. Close it to tear down the |
| 136 | * TAP interface. |
| 137 | */ |
| 138 | @Override |
| 139 | public TestNetworkInterface createTapInterface() { |
| 140 | return createInterface(false, new LinkAddress[0]); |
| 141 | } |
| 142 | |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 143 | // Tracker for TestNetworkAgents |
| 144 | @GuardedBy("mTestNetworkTracker") |
| 145 | @NonNull |
| 146 | private final SparseArray<TestNetworkAgent> mTestNetworkTracker = new SparseArray<>(); |
| 147 | |
| 148 | public class TestNetworkAgent extends NetworkAgent implements IBinder.DeathRecipient { |
| 149 | private static final int NETWORK_SCORE = 1; // Use a low, non-zero score. |
| 150 | |
| 151 | private final int mUid; |
| 152 | @NonNull private final NetworkInfo mNi; |
| 153 | @NonNull private final NetworkCapabilities mNc; |
| 154 | @NonNull private final LinkProperties mLp; |
| 155 | |
| 156 | @GuardedBy("mBinderLock") |
| 157 | @NonNull |
| 158 | private IBinder mBinder; |
| 159 | |
| 160 | @NonNull private final Object mBinderLock = new Object(); |
| 161 | |
| 162 | private TestNetworkAgent( |
| 163 | @NonNull Looper looper, |
| 164 | @NonNull Context context, |
| 165 | @NonNull NetworkInfo ni, |
| 166 | @NonNull NetworkCapabilities nc, |
| 167 | @NonNull LinkProperties lp, |
| 168 | int uid, |
| 169 | @NonNull IBinder binder) |
| 170 | throws RemoteException { |
| 171 | super(looper, context, TEST_NETWORK_TYPE, ni, nc, lp, NETWORK_SCORE); |
| 172 | |
| 173 | mUid = uid; |
| 174 | mNi = ni; |
| 175 | mNc = nc; |
| 176 | mLp = lp; |
| 177 | |
| 178 | synchronized (mBinderLock) { |
| 179 | mBinder = binder; // Binder null-checks in create() |
| 180 | |
| 181 | try { |
| 182 | mBinder.linkToDeath(this, 0); |
| 183 | } catch (RemoteException e) { |
| 184 | binderDied(); |
| 185 | throw e; // Abort, signal failure up the stack. |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * If the Binder object dies, this function is called to free the resources of this |
| 192 | * TestNetworkAgent |
| 193 | */ |
| 194 | @Override |
| 195 | public void binderDied() { |
| 196 | teardown(); |
| 197 | } |
| 198 | |
| 199 | @Override |
| 200 | protected void unwanted() { |
| 201 | teardown(); |
| 202 | } |
| 203 | |
| 204 | private void teardown() { |
| 205 | mNi.setDetailedState(DetailedState.DISCONNECTED, null, null); |
| 206 | mNi.setIsAvailable(false); |
| 207 | sendNetworkInfo(mNi); |
| 208 | |
| 209 | // Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than |
| 210 | // once (otherwise it could throw an exception) |
| 211 | synchronized (mBinderLock) { |
| 212 | // If mBinder is null, this Test Network has already been cleaned up. |
| 213 | if (mBinder == null) return; |
| 214 | mBinder.unlinkToDeath(this, 0); |
| 215 | mBinder = null; |
| 216 | } |
| 217 | |
| 218 | // Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up |
| 219 | // resources, even for binder death or unwanted calls. |
| 220 | synchronized (mTestNetworkTracker) { |
Chalard Jean | a0e2aa12 | 2019-12-13 19:47:12 +0900 | [diff] [blame] | 221 | mTestNetworkTracker.remove(network.netId); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | private TestNetworkAgent registerTestNetworkAgent( |
| 227 | @NonNull Looper looper, |
| 228 | @NonNull Context context, |
| 229 | @NonNull String iface, |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 230 | @Nullable LinkProperties lp, |
| 231 | boolean isMetered, |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 232 | int callingUid, |
| 233 | @NonNull IBinder binder) |
| 234 | throws RemoteException, SocketException { |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 235 | Objects.requireNonNull(looper, "missing Looper"); |
| 236 | Objects.requireNonNull(context, "missing Context"); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 237 | // iface and binder validity checked by caller |
| 238 | |
| 239 | // Build network info with special testing type |
| 240 | NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_TEST, 0, TEST_NETWORK_TYPE, ""); |
| 241 | ni.setDetailedState(DetailedState.CONNECTED, null, null); |
| 242 | ni.setIsAvailable(true); |
| 243 | |
| 244 | // Build narrow set of NetworkCapabilities, useful only for testing |
| 245 | NetworkCapabilities nc = new NetworkCapabilities(); |
| 246 | nc.clearAll(); // Remove default capabilities. |
| 247 | nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST); |
| 248 | nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); |
| 249 | nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
| 250 | nc.setNetworkSpecifier(new StringNetworkSpecifier(iface)); |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 251 | if (!isMetered) { |
| 252 | nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| 253 | } |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 254 | |
| 255 | // Build LinkProperties |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 256 | if (lp == null) { |
| 257 | lp = new LinkProperties(); |
| 258 | } else { |
| 259 | lp = new LinkProperties(lp); |
| 260 | // Use LinkAddress(es) from the interface itself to minimize how much the caller |
| 261 | // is trusted. |
| 262 | lp.setLinkAddresses(new ArrayList<>()); |
| 263 | } |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 264 | lp.setInterfaceName(iface); |
| 265 | |
| 266 | // Find the currently assigned addresses, and add them to LinkProperties |
| 267 | boolean allowIPv4 = false, allowIPv6 = false; |
| 268 | NetworkInterface netIntf = NetworkInterface.getByName(iface); |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 269 | Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 270 | |
| 271 | for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) { |
| 272 | lp.addLinkAddress( |
| 273 | new LinkAddress(intfAddr.getAddress(), intfAddr.getNetworkPrefixLength())); |
| 274 | |
| 275 | if (intfAddr.getAddress() instanceof Inet6Address) { |
| 276 | allowIPv6 |= !intfAddr.getAddress().isLinkLocalAddress(); |
| 277 | } else if (intfAddr.getAddress() instanceof Inet4Address) { |
| 278 | allowIPv4 = true; |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | // Add global routes (but as non-default, non-internet providing network) |
| 283 | if (allowIPv4) { |
| 284 | lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface)); |
| 285 | } |
| 286 | if (allowIPv6) { |
| 287 | lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface)); |
| 288 | } |
| 289 | |
| 290 | return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder); |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 291 | } |
| 292 | |
| 293 | /** |
| 294 | * Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS |
| 295 | * permission. |
| 296 | * |
| 297 | * <p>This method provides a Network that is useful only for testing. |
| 298 | */ |
| 299 | @Override |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 300 | public void setupTestNetwork( |
| 301 | @NonNull String iface, |
| 302 | @Nullable LinkProperties lp, |
| 303 | boolean isMetered, |
| 304 | @NonNull IBinder binder) { |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 305 | enforceTestNetworkPermissions(mContext); |
| 306 | |
Daulet Zhanguzin | ea1a7ca | 2020-01-03 09:46:50 +0000 | [diff] [blame] | 307 | Objects.requireNonNull(iface, "missing Iface"); |
| 308 | Objects.requireNonNull(binder, "missing IBinder"); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 309 | |
| 310 | if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX) |
| 311 | || iface.startsWith(TEST_TUN_PREFIX))) { |
| 312 | throw new IllegalArgumentException( |
| 313 | "Cannot create network for non ipsec, non-testtun interface"); |
| 314 | } |
| 315 | |
| 316 | // Setup needs to be done with NETWORK_STACK privileges. |
| 317 | int callingUid = Binder.getCallingUid(); |
| 318 | Binder.withCleanCallingIdentity( |
| 319 | () -> { |
| 320 | try { |
| 321 | mNMS.setInterfaceUp(iface); |
| 322 | |
| 323 | // Synchronize all accesses to mTestNetworkTracker to prevent the case |
| 324 | // where: |
| 325 | // 1. TestNetworkAgent successfully binds to death of binder |
| 326 | // 2. Before it is added to the mTestNetworkTracker, binder dies, |
| 327 | // binderDied() is called (on a different thread) |
| 328 | // 3. This thread is pre-empted, put() is called after remove() |
| 329 | synchronized (mTestNetworkTracker) { |
| 330 | TestNetworkAgent agent = |
| 331 | registerTestNetworkAgent( |
| 332 | mHandler.getLooper(), |
| 333 | mContext, |
| 334 | iface, |
Benedict Wong | 512ab0d | 2019-04-18 19:18:43 -0700 | [diff] [blame] | 335 | lp, |
| 336 | isMetered, |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 337 | callingUid, |
| 338 | binder); |
| 339 | |
Chalard Jean | a0e2aa12 | 2019-12-13 19:47:12 +0900 | [diff] [blame] | 340 | mTestNetworkTracker.put(agent.network.netId, agent); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 341 | } |
| 342 | } catch (SocketException e) { |
| 343 | throw new UncheckedIOException(e); |
| 344 | } catch (RemoteException e) { |
| 345 | throw e.rethrowFromSystemServer(); |
| 346 | } |
| 347 | }); |
| 348 | } |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 349 | |
| 350 | /** Teardown a test network */ |
| 351 | @Override |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 352 | public void teardownTestNetwork(int netId) { |
| 353 | enforceTestNetworkPermissions(mContext); |
| 354 | |
Benedict Wong | 7df36ed | 2019-03-12 21:54:16 -0700 | [diff] [blame] | 355 | final TestNetworkAgent agent; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 356 | synchronized (mTestNetworkTracker) { |
| 357 | agent = mTestNetworkTracker.get(netId); |
| 358 | } |
| 359 | |
| 360 | if (agent == null) { |
| 361 | return; // Already torn down |
| 362 | } else if (agent.mUid != Binder.getCallingUid()) { |
| 363 | throw new SecurityException("Attempted to modify other user's test networks"); |
| 364 | } |
| 365 | |
| 366 | // Safe to be called multiple times. |
| 367 | agent.teardown(); |
| 368 | } |
| 369 | |
Benedict Wong | 3ec38dc | 2019-04-09 16:29:43 -0700 | [diff] [blame] | 370 | private static final String PERMISSION_NAME = |
| 371 | android.Manifest.permission.MANAGE_TEST_NETWORKS; |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 372 | |
| 373 | public static void enforceTestNetworkPermissions(@NonNull Context context) { |
Benedict Wong | 3ec38dc | 2019-04-09 16:29:43 -0700 | [diff] [blame] | 374 | context.enforceCallingOrSelfPermission(PERMISSION_NAME, "TestNetworkService"); |
Benedict Wong | 6c24213 | 2018-11-14 17:53:19 -0800 | [diff] [blame] | 375 | } |
Benedict Wong | e40eab6 | 2018-11-14 17:50:13 -0800 | [diff] [blame] | 376 | } |