summaryrefslogtreecommitdiff
path: root/core/java/android/net/VpnService.java
blob: 2d9860cf0e40f53193c4ebf1548c013b05490d3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;

import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.net.Network;
import android.net.NetworkUtils;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;

import com.android.internal.net.VpnConfig;

import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * VpnService is a base class for applications to extend and build their
 * own VPN solutions. In general, it creates a virtual network interface,
 * configures addresses and routing rules, and returns a file descriptor
 * to the application. Each read from the descriptor retrieves an outgoing
 * packet which was routed to the interface. Each write to the descriptor
 * injects an incoming packet just like it was received from the interface.
 * The interface is running on Internet Protocol (IP), so packets are
 * always started with IP headers. The application then completes a VPN
 * connection by processing and exchanging packets with the remote server
 * over a tunnel.
 *
 * <p>Letting applications intercept packets raises huge security concerns.
 * A VPN application can easily break the network. Besides, two of them may
 * conflict with each other. The system takes several actions to address
 * these issues. Here are some key points:
 * <ul>
 *   <li>User action is required the first time an application creates a VPN
 *       connection.</li>
 *   <li>There can be only one VPN connection running at the same time. The
 *       existing interface is deactivated when a new one is created.</li>
 *   <li>A system-managed notification is shown during the lifetime of a
 *       VPN connection.</li>
 *   <li>A system-managed dialog gives the information of the current VPN
 *       connection. It also provides a button to disconnect.</li>
 *   <li>The network is restored automatically when the file descriptor is
 *       closed. It also covers the cases when a VPN application is crashed
 *       or killed by the system.</li>
 * </ul>
 *
 * <p>There are two primary methods in this class: {@link #prepare} and
 * {@link Builder#establish}. The former deals with user action and stops
 * the VPN connection created by another application. The latter creates
 * a VPN interface using the parameters supplied to the {@link Builder}.
 * An application must call {@link #prepare} to grant the right to use
 * other methods in this class, and the right can be revoked at any time.
 * Here are the general steps to create a VPN connection:
 * <ol>
 *   <li>When the user presses the button to connect, call {@link #prepare}
 *       and launch the returned intent, if non-null.</li>
 *   <li>When the application becomes prepared, start the service.</li>
 *   <li>Create a tunnel to the remote server and negotiate the network
 *       parameters for the VPN connection.</li>
 *   <li>Supply those parameters to a {@link Builder} and create a VPN
 *       interface by calling {@link Builder#establish}.</li>
 *   <li>Process and exchange packets between the tunnel and the returned
 *       file descriptor.</li>
 *   <li>When {@link #onRevoke} is invoked, close the file descriptor and
 *       shut down the tunnel gracefully.</li>
 * </ol>
 *
 * <p>Services extended this class need to be declared with appropriate
 * permission and intent filter. Their access must be secured by
 * {@link android.Manifest.permission#BIND_VPN_SERVICE} permission, and
 * their intent filter must match {@link #SERVICE_INTERFACE} action. Here
 * is an example of declaring a VPN service in {@code AndroidManifest.xml}:
 * <pre>
 * &lt;service android:name=".ExampleVpnService"
 *         android:permission="android.permission.BIND_VPN_SERVICE"&gt;
 *     &lt;intent-filter&gt;
 *         &lt;action android:name="android.net.VpnService"/&gt;
 *     &lt;/intent-filter&gt;
 * &lt;/service&gt;</pre>
 *
 * @see Builder
 */
public class VpnService extends Service {

    /**
     * The action must be matched by the intent filter of this service. It also
     * needs to require {@link android.Manifest.permission#BIND_VPN_SERVICE}
     * permission so that other applications cannot abuse it.
     */
    public static final String SERVICE_INTERFACE = VpnConfig.SERVICE_INTERFACE;

    /**
     * Use IConnectivityManager since those methods are hidden and not
     * available in ConnectivityManager.
     */
    private static IConnectivityManager getService() {
        return IConnectivityManager.Stub.asInterface(
                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
    }

    /**
     * Prepare to establish a VPN connection. This method returns {@code null}
     * if the VPN application is already prepared or if the user has previously
     * consented to the VPN application. Otherwise, it returns an
     * {@link Intent} to a system activity. The application should launch the
     * activity using {@link Activity#startActivityForResult} to get itself
     * prepared. The activity may pop up a dialog to require user action, and
     * the result will come back via its {@link Activity#onActivityResult}.
     * If the result is {@link Activity#RESULT_OK}, the application becomes
     * prepared and is granted to use other methods in this class.
     *
     * <p>Only one application can be granted at the same time. The right
     * is revoked when another application is granted. The application
     * losing the right will be notified via its {@link #onRevoke}. Unless
     * it becomes prepared again, subsequent calls to other methods in this
     * class will fail.
     *
     * <p>The user may disable the VPN at any time while it is activated, in
     * which case this method will return an intent the next time it is
     * executed to obtain the user's consent again.
     *
     * @see #onRevoke
     */
    public static Intent prepare(Context context) {
        try {
            if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
                return null;
            }
        } catch (RemoteException e) {
            // ignore
        }
        return VpnConfig.getIntentForConfirmation();
    }

    /**
     * Version of {@link #prepare(Context)} which does not require user consent.
     *
     * <p>Requires {@link android.Manifest.permission#CONTROL_VPN} and should generally not be
     * used. Only acceptable in situations where user consent has been obtained through other means.
     *
     * <p>Once this is run, future preparations may be done with the standard prepare method as this
     * will authorize the package to prepare the VPN without consent in the future.
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.CONTROL_VPN)
    public static void prepareAndAuthorize(Context context) {
        IConnectivityManager cm = getService();
        String packageName = context.getPackageName();
        try {
            // Only prepare if we're not already prepared.
            int userId = UserHandle.myUserId();
            if (!cm.prepareVpn(packageName, null, userId)) {
                cm.prepareVpn(null, packageName, userId);
            }
            cm.setVpnPackageAuthorization(packageName, userId, true);
        } catch (RemoteException e) {
            // ignore
        }
    }

    /**
     * Protect a socket from VPN connections. After protecting, data sent
     * through this socket will go directly to the underlying network,
     * so its traffic will not be forwarded through the VPN.
     * This method is useful if some connections need to be kept
     * outside of VPN. For example, a VPN tunnel should protect itself if its
     * destination is covered by VPN routes. Otherwise its outgoing packets
     * will be sent back to the VPN interface and cause an infinite loop. This
     * method will fail if the application is not prepared or is revoked.
     *
     * <p class="note">The socket is NOT closed by this method.
     *
     * @return {@code true} on success.
     */
    public boolean protect(int socket) {
        return NetworkUtils.protectFromVpn(socket);
    }

    /**
     * Convenience method to protect a {@link Socket} from VPN connections.
     *
     * @return {@code true} on success.
     * @see #protect(int)
     */
    public boolean protect(Socket socket) {
        return protect(socket.getFileDescriptor$().getInt$());
    }

    /**
     * Convenience method to protect a {@link DatagramSocket} from VPN
     * connections.
     *
     * @return {@code true} on success.
     * @see #protect(int)
     */
    public boolean protect(DatagramSocket socket) {
        return protect(socket.getFileDescriptor$().getInt$());
    }

    /**
     * Adds a network address to the VPN interface.
     *
     * Both IPv4 and IPv6 addresses are supported. The VPN must already be established. Fails if the
     * address is already in use or cannot be assigned to the interface for any other reason.
     *
     * Adding an address implicitly allows traffic from that address family (i.e., IPv4 or IPv6) to
     * be routed over the VPN. @see Builder#allowFamily
     *
     * @throws IllegalArgumentException if the address is invalid.
     *
     * @param address The IP address (IPv4 or IPv6) to assign to the VPN interface.
     * @param prefixLength The prefix length of the address.
     *
     * @return {@code true} on success.
     * @see Builder#addAddress
     *
     * @hide
     */
    public boolean addAddress(InetAddress address, int prefixLength) {
        check(address, prefixLength);
        try {
            return getService().addVpnAddress(address.getHostAddress(), prefixLength);
        } catch (RemoteException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Removes a network address from the VPN interface.
     *
     * Both IPv4 and IPv6 addresses are supported. The VPN must already be established. Fails if the
     * address is not assigned to the VPN interface, or if it is the only address assigned (thus
     * cannot be removed), or if the address cannot be removed for any other reason.
     *
     * After removing an address, if there are no addresses, routes or DNS servers of a particular
     * address family (i.e., IPv4 or IPv6) configured on the VPN, that <b>DOES NOT</b> block that
     * family from being routed. In other words, once an address family has been allowed, it stays
     * allowed for the rest of the VPN's session. @see Builder#allowFamily
     *
     * @throws IllegalArgumentException if the address is invalid.
     *
     * @param address The IP address (IPv4 or IPv6) to assign to the VPN interface.
     * @param prefixLength The prefix length of the address.
     *
     * @return {@code true} on success.
     *
     * @hide
     */
    public boolean removeAddress(InetAddress address, int prefixLength) {
        check(address, prefixLength);
        try {
            return getService().removeVpnAddress(address.getHostAddress(), prefixLength);
        } catch (RemoteException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Sets the underlying networks used by the VPN for its upstream connections.
     *
     * <p>Used by the system to know the actual networks that carry traffic for apps affected by
     * this VPN in order to present this information to the user (e.g., via status bar icons).
     *
     * <p>This method only needs to be called if the VPN has explicitly bound its underlying
     * communications channels &mdash; such as the socket(s) passed to {@link #protect(int)} &mdash;
     * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or
     * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time
     * the set of {@code Network}s it is using changes.
     *
     * <p>{@code networks} is one of the following:
     * <ul>
     * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
     * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
     * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
     * first in the array.</li>
     * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
     * underlying network connection, and thus, app traffic will not be sent or received.</li>
     * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
     * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
     * APIs mentioned above to send traffic over specific channels.</li>
     * </ul>
     *
     * <p>This call will succeed only if the VPN is currently established. For setting this value
     * when the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
     *
     * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
     *
     * @return {@code true} on success.
     */
    public boolean setUnderlyingNetworks(Network[] networks) {
        try {
            return getService().setUnderlyingNetworksForVpn(networks);
        } catch (RemoteException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Return the communication interface to the service. This method returns
     * {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE}
     * action. Applications overriding this method must identify the intent
     * and return the corresponding interface accordingly.
     *
     * @see Service#onBind
     */
    @Override
    public IBinder onBind(Intent intent) {
        if (intent != null && SERVICE_INTERFACE.equals(intent.getAction())) {
            return new Callback();
        }
        return null;
    }

    /**
     * Invoked when the application is revoked. At this moment, the VPN
     * interface is already deactivated by the system. The application should
     * close the file descriptor and shut down gracefully. The default
     * implementation of this method is calling {@link Service#stopSelf()}.
     *
     * <p class="note">Calls to this method may not happen on the main thread
     * of the process.
     *
     * @see #prepare
     */
    public void onRevoke() {
        stopSelf();
    }

    /**
     * Use raw Binder instead of AIDL since now there is only one usage.
     */
    private class Callback extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
            if (code == IBinder.LAST_CALL_TRANSACTION) {
                onRevoke();
                return true;
            }
            return false;
        }
    }

    /**
     * Private method to validate address and prefixLength.
     */
    private static void check(InetAddress address, int prefixLength) {
        if (address.isLoopbackAddress()) {
            throw new IllegalArgumentException("Bad address");
        }
        if (address instanceof Inet4Address) {
            if (prefixLength < 0 || prefixLength > 32) {
                throw new IllegalArgumentException("Bad prefixLength");
            }
        } else if (address instanceof Inet6Address) {
            if (prefixLength < 0 || prefixLength > 128) {
                throw new IllegalArgumentException("Bad prefixLength");
            }
        } else {
            throw new IllegalArgumentException("Unsupported family");
        }
    }

    /**
     * Helper class to create a VPN interface. This class should be always
     * used within the scope of the outer {@link VpnService}.
     *
     * @see VpnService
     */
    public class Builder {

        private final VpnConfig mConfig = new VpnConfig();
        private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
        private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();

        public Builder() {
            mConfig.user = VpnService.this.getClass().getName();
        }

        /**
         * Set the name of this session. It will be displayed in
         * system-managed dialogs and notifications. This is recommended
         * not required.
         */
        public Builder setSession(String session) {
            mConfig.session = session;
            return this;
        }

        /**
         * Set the {@link PendingIntent} to an activity for users to
         * configure the VPN connection. If it is not set, the button
         * to configure will not be shown in system-managed dialogs.
         */
        public Builder setConfigureIntent(PendingIntent intent) {
            mConfig.configureIntent = intent;
            return this;
        }

        /**
         * Set the maximum transmission unit (MTU) of the VPN interface. If
         * it is not set, the default value in the operating system will be
         * used.
         *
         * @throws IllegalArgumentException if the value is not positive.
         */
        public Builder setMtu(int mtu) {
            if (mtu <= 0) {
                throw new IllegalArgumentException("Bad mtu");
            }
            mConfig.mtu = mtu;
            return this;
        }

        /**
         * Add a network address to the VPN interface. Both IPv4 and IPv6
         * addresses are supported. At least one address must be set before
         * calling {@link #establish}.
         *
         * Adding an address implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the address is invalid.
         */
        public Builder addAddress(InetAddress address, int prefixLength) {
            check(address, prefixLength);

            if (address.isAnyLocalAddress()) {
                throw new IllegalArgumentException("Bad address");
            }
            mAddresses.add(new LinkAddress(address, prefixLength));
            mConfig.updateAllowedFamilies(address);
            return this;
        }

        /**
         * Convenience method to add a network address to the VPN interface
         * using a numeric address string. See {@link InetAddress} for the
         * definitions of numeric address formats.
         *
         * Adding an address implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the address is invalid.
         * @see #addAddress(InetAddress, int)
         */
        public Builder addAddress(String address, int prefixLength) {
            return addAddress(InetAddress.parseNumericAddress(address), prefixLength);
        }

        /**
         * Add a network route to the VPN interface. Both IPv4 and IPv6
         * routes are supported.
         *
         * Adding a route implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the route is invalid.
         */
        public Builder addRoute(InetAddress address, int prefixLength) {
            check(address, prefixLength);

            int offset = prefixLength / 8;
            byte[] bytes = address.getAddress();
            if (offset < bytes.length) {
                for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
                    if (bytes[offset] != 0) {
                        throw new IllegalArgumentException("Bad address");
                    }
                }
            }
            mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null));
            mConfig.updateAllowedFamilies(address);
            return this;
        }

        /**
         * Convenience method to add a network route to the VPN interface
         * using a numeric address string. See {@link InetAddress} for the
         * definitions of numeric address formats.
         *
         * Adding a route implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the route is invalid.
         * @see #addRoute(InetAddress, int)
         */
        public Builder addRoute(String address, int prefixLength) {
            return addRoute(InetAddress.parseNumericAddress(address), prefixLength);
        }

        /**
         * Add a DNS server to the VPN connection. Both IPv4 and IPv6
         * addresses are supported. If none is set, the DNS servers of
         * the default network will be used.
         *
         * Adding a server implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the address is invalid.
         */
        public Builder addDnsServer(InetAddress address) {
            if (address.isLoopbackAddress() || address.isAnyLocalAddress()) {
                throw new IllegalArgumentException("Bad address");
            }
            if (mConfig.dnsServers == null) {
                mConfig.dnsServers = new ArrayList<String>();
            }
            mConfig.dnsServers.add(address.getHostAddress());
            return this;
        }

        /**
         * Convenience method to add a DNS server to the VPN connection
         * using a numeric address string. See {@link InetAddress} for the
         * definitions of numeric address formats.
         *
         * Adding a server implicitly allows traffic from that address family
         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
         *
         * @throws IllegalArgumentException if the address is invalid.
         * @see #addDnsServer(InetAddress)
         */
        public Builder addDnsServer(String address) {
            return addDnsServer(InetAddress.parseNumericAddress(address));
        }

        /**
         * Add a search domain to the DNS resolver.
         */
        public Builder addSearchDomain(String domain) {
            if (mConfig.searchDomains == null) {
                mConfig.searchDomains = new ArrayList<String>();
            }
            mConfig.searchDomains.add(domain);
            return this;
        }

        /**
         * Allows traffic from the specified address family.
         *
         * By default, if no address, route or DNS server of a specific family (IPv4 or IPv6) is
         * added to this VPN, then all outgoing traffic of that family is blocked. If any address,
         * route or DNS server is added, that family is allowed.
         *
         * This method allows an address family to be unblocked even without adding an address,
         * route or DNS server of that family. Traffic of that family will then typically
         * fall-through to the underlying network if it's supported.
         *
         * {@code family} must be either {@code AF_INET} (for IPv4) or {@code AF_INET6} (for IPv6).
         * {@link IllegalArgumentException} is thrown if it's neither.
         *
         * @param family The address family ({@code AF_INET} or {@code AF_INET6}) to allow.
         *
         * @return this {@link Builder} object to facilitate chaining of method calls.
         */
        public Builder allowFamily(int family) {
            if (family == AF_INET) {
                mConfig.allowIPv4 = true;
            } else if (family == AF_INET6) {
                mConfig.allowIPv6 = true;
            } else {
                throw new IllegalArgumentException(family + " is neither " + AF_INET + " nor " +
                        AF_INET6);
            }
            return this;
        }

        private void verifyApp(String packageName) throws PackageManager.NameNotFoundException {
            IPackageManager pm = IPackageManager.Stub.asInterface(
                    ServiceManager.getService("package"));
            try {
                pm.getApplicationInfo(packageName, 0, UserHandle.getCallingUserId());
            } catch (RemoteException e) {
                throw new IllegalStateException(e);
            }
        }

        /**
         * Adds an application that's allowed to access the VPN connection.
         *
         * If this method is called at least once, only applications added through this method (and
         * no others) are allowed access. Else (if this method is never called), all applications
         * are allowed by default.  If some applications are added, other, un-added applications
         * will use networking as if the VPN wasn't running.
         *
         * A {@link Builder} may have only a set of allowed applications OR a set of disallowed
         * ones, but not both. Calling this method after {@link #addDisallowedApplication} has
         * already been called, or vice versa, will throw an {@link UnsupportedOperationException}.
         *
         * {@code packageName} must be the canonical name of a currently installed application.
         * {@link PackageManager.NameNotFoundException} is thrown if there's no such application.
         *
         * @throws PackageManager.NameNotFoundException If the application isn't installed.
         *
         * @param packageName The full name (e.g.: "com.google.apps.contacts") of an application.
         *
         * @return this {@link Builder} object to facilitate chaining method calls.
         */
        public Builder addAllowedApplication(String packageName)
                throws PackageManager.NameNotFoundException {
            if (mConfig.disallowedApplications != null) {
                throw new UnsupportedOperationException("addDisallowedApplication already called");
            }
            verifyApp(packageName);
            if (mConfig.allowedApplications == null) {
                mConfig.allowedApplications = new ArrayList<String>();
            }
            mConfig.allowedApplications.add(packageName);
            return this;
        }

        /**
         * Adds an application that's denied access to the VPN connection.
         *
         * By default, all applications are allowed access, except for those denied through this
         * method.  Denied applications will use networking as if the VPN wasn't running.
         *
         * A {@link Builder} may have only a set of allowed applications OR a set of disallowed
         * ones, but not both. Calling this method after {@link #addAllowedApplication} has already
         * been called, or vice versa, will throw an {@link UnsupportedOperationException}.
         *
         * {@code packageName} must be the canonical name of a currently installed application.
         * {@link PackageManager.NameNotFoundException} is thrown if there's no such application.
         *
         * @throws PackageManager.NameNotFoundException If the application isn't installed.
         *
         * @param packageName The full name (e.g.: "com.google.apps.contacts") of an application.
         *
         * @return this {@link Builder} object to facilitate chaining method calls.
         */
        public Builder addDisallowedApplication(String packageName)
                throws PackageManager.NameNotFoundException {
            if (mConfig.allowedApplications != null) {
                throw new UnsupportedOperationException("addAllowedApplication already called");
            }
            verifyApp(packageName);
            if (mConfig.disallowedApplications == null) {
                mConfig.disallowedApplications = new ArrayList<String>();
            }
            mConfig.disallowedApplications.add(packageName);
            return this;
        }

        /**
         * Allows all apps to bypass this VPN connection.
         *
         * By default, all traffic from apps is forwarded through the VPN interface and it is not
         * possible for apps to side-step the VPN. If this method is called, apps may use methods
         * such as {@link ConnectivityManager#bindProcessToNetwork} to instead send/receive
         * directly over the underlying network or any other network they have permissions for.
         *
         * @return this {@link Builder} object to facilitate chaining of method calls.
         */
        public Builder allowBypass() {
            mConfig.allowBypass = true;
            return this;
        }

        /**
         * Sets the VPN interface's file descriptor to be in blocking/non-blocking mode.
         *
         * By default, the file descriptor returned by {@link #establish} is non-blocking.
         *
         * @param blocking True to put the descriptor into blocking mode; false for non-blocking.
         *
         * @return this {@link Builder} object to facilitate chaining method calls.
         */
        public Builder setBlocking(boolean blocking) {
            mConfig.blocking = blocking;
            return this;
        }

        /**
         * Sets the underlying networks used by the VPN for its upstream connections.
         *
         * @see VpnService#setUnderlyingNetworks
         *
         * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
         *
         * @return this {@link Builder} object to facilitate chaining method calls.
         */
        public Builder setUnderlyingNetworks(Network[] networks) {
            mConfig.underlyingNetworks = networks != null ? networks.clone() : null;
            return this;
        }

        /**
         * Create a VPN interface using the parameters supplied to this
         * builder. The interface works on IP packets, and a file descriptor
         * is returned for the application to access them. Each read
         * retrieves an outgoing packet which was routed to the interface.
         * Each write injects an incoming packet just like it was received
         * from the interface. The file descriptor is put into non-blocking
         * mode by default to avoid blocking Java threads. To use the file
         * descriptor completely in native space, see
         * {@link ParcelFileDescriptor#detachFd()}. The application MUST
         * close the file descriptor when the VPN connection is terminated.
         * The VPN interface will be removed and the network will be
         * restored by the system automatically.
         *
         * <p>To avoid conflicts, there can be only one active VPN interface
         * at the same time. Usually network parameters are never changed
         * during the lifetime of a VPN connection. It is also common for an
         * application to create a new file descriptor after closing the
         * previous one. However, it is rare but not impossible to have two
         * interfaces while performing a seamless handover. In this case, the
         * old interface will be deactivated when the new one is created
         * successfully. Both file descriptors are valid but now outgoing
         * packets will be routed to the new interface. Therefore, after
         * draining the old file descriptor, the application MUST close it
         * and start using the new file descriptor. If the new interface
         * cannot be created, the existing interface and its file descriptor
         * remain untouched.
         *
         * <p>An exception will be thrown if the interface cannot be created
         * for any reason. However, this method returns {@code null} if the
         * application is not prepared or is revoked. This helps solve
         * possible race conditions between other VPN applications.
         *
         * @return {@link ParcelFileDescriptor} of the VPN interface, or
         *         {@code null} if the application is not prepared.
         * @throws IllegalArgumentException if a parameter is not accepted
         *         by the operating system.
         * @throws IllegalStateException if a parameter cannot be applied
         *         by the operating system.
         * @throws SecurityException if the service is not properly declared
         *         in {@code AndroidManifest.xml}.
         * @see VpnService
         */
        public ParcelFileDescriptor establish() {
            mConfig.addresses = mAddresses;
            mConfig.routes = mRoutes;

            try {
                return getService().establishVpn(mConfig);
            } catch (RemoteException e) {
                throw new IllegalStateException(e);
            }
        }
    }
}