Android cung cấp API để nhà phát triển tạo ra các giải pháp mạng riêng ảo (VPN). Tài liệu này giải thích cách phát triển và kiểm thử ứng dụng VPN của riêng bạn cho các thiết bị chạy Android.
Tổng quan
VPN cho phép các thiết bị không có mặt thực tế trên mạng truy cập mạng một cách an toàn.
Android có một ứng dụng VPN tích hợp sẵn (PPTP và L2TP/IPSec), đôi khi được gọi là VPN cũ. Android 4.0 (cấp độ API 14) đã ra mắt các API để nhà phát triển ứng dụng có thể cung cấp giải pháp VPN của riêng họ. Bạn đóng gói giải pháp VPN của mình thành một ứng dụng mà người dùng cài đặt trên thiết bị. Nhà phát triển thường xây dựng ứng dụng VPN vì một trong những lý do sau:
- Để cung cấp các giao thức VPN mà ứng dụng tích hợp sẵn không hỗ trợ.
- Để giúp mọi người kết nối với dịch vụ VPN mà không cần cấu hình phức tạp.
Phần còn lại của tài liệu này giải thích cách phát triển ứng dụng VPN (bao gồm cả VPN luôn bật và VPN theo ứng dụng) và không đề cập đến ứng dụng VPN tích hợp sẵn.
Trải nghiệm người dùng
Android cung cấp một giao diện người dùng (UI) để giúp người dùng định cấu hình, khởi động và dừng giải pháp VPN của bạn. Giao diện người dùng hệ thống cũng cho người dùng thiết bị biết rằng có một kết nối VPN đang hoạt động. Android cho thấy các thành phần giao diện người dùng sau đây cho kết nối VPN:
- Trước khi ứng dụng VPN có thể hoạt động lần đầu tiên, hệ thống sẽ hiển thị hộp thoại yêu cầu kết nối. Hộp thoại này nhắc người dùng thiết bị xác nhận rằng họ tin tưởng VPN và chấp nhận yêu cầu.
- Màn hình cài đặt VPN (Cài đặt > Mạng và Internet > VPN) cho thấy các ứng dụng VPN mà người dùng đã chấp nhận yêu cầu kết nối. Có một nút để định cấu hình các lựa chọn của hệ thống hoặc xoá VPN.
- Khay Cài đặt nhanh hiển thị một bảng thông tin khi có kết nối đang hoạt động. Khi bạn nhấn vào nhãn, một hộp thoại sẽ xuất hiện, cung cấp thêm thông tin và đường liên kết đến phần Cài đặt.
- Thanh trạng thái có biểu tượng VPN (khoá) để cho biết kết nối đang hoạt động.
Ứng dụng của bạn cũng cần cung cấp giao diện người dùng để người dùng thiết bị có thể định cấu hình các tuỳ chọn của dịch vụ. Ví dụ: giải pháp của bạn có thể cần thu thập chế độ cài đặt xác thực tài khoản. Thêm các phần tử trên giao diện người dùng sau đây vào ứng dụng của bạn:
- Các chế độ điều khiển để bắt đầu và dừng kết nối theo cách thủ công. VPN luôn bật có thể kết nối khi cần, nhưng hãy cho phép người dùng định cấu hình kết nối trong lần đầu tiên họ sử dụng VPN của bạn.
- Thông báo không thể đóng khi dịch vụ đang hoạt động. Thông báo có thể cho biết trạng thái kết nối hoặc cung cấp thêm thông tin, chẳng hạn như số liệu thống kê về mạng. Khi người dùng nhấn vào thông báo, ứng dụng của bạn sẽ chạy ở nền trước. Xoá thông báo sau khi dịch vụ ngừng hoạt động.
Dịch vụ VPN
Ứng dụng của bạn kết nối mạng hệ thống cho người dùng (hoặc hồ sơ công việc) với cổng VPN. Mỗi người dùng (hoặc hồ sơ công việc) có thể chạy một ứng dụng VPN khác nhau. Bạn tạo một dịch vụ VPN mà hệ thống sử dụng để khởi động và dừng VPN, đồng thời theo dõi trạng thái kết nối. Dịch vụ VPN của bạn kế thừa từ VpnService.
Dịch vụ này cũng đóng vai trò là vùng chứa cho các kết nối cổng VPN và giao diện thiết bị cục bộ của các kết nối đó. Thực thể dịch vụ của bạn gọi các phương thức VpnService.Builder để thiết lập một giao diện cục bộ mới.
VpnService kết nối mạng Android với cổng VPNỨng dụng của bạn chuyển dữ liệu sau đây để kết nối thiết bị với cổng VPN:
- Đọc các gói IP gửi đi từ chỉ số mô tả tệp của giao diện cục bộ, mã hoá các gói đó rồi gửi đến cổng VPN.
- Ghi các gói dữ liệu đến (đã nhận và giải mã từ cổng VPN) vào chỉ số mô tả tệp của giao diện cục bộ.
Mỗi người dùng hoặc hồ sơ chỉ có một dịch vụ đang hoạt động. Khi bạn bắt đầu một dịch vụ mới, dịch vụ đang hoạt động sẽ tự động dừng.
Thêm dịch vụ
Để thêm dịch vụ VPN vào ứng dụng, hãy tạo một dịch vụ Android kế thừa từ VpnService. Khai báo dịch vụ VPN trong tệp kê khai ứng dụng bằng cách thêm các nội dung sau:
- Bảo vệ dịch vụ bằng quyền
BIND_VPN_SERVICEđể chỉ hệ thống mới có thể liên kết với dịch vụ của bạn. - Quảng cáo dịch vụ bằng bộ lọc ý định
android.net.VpnServiceđể hệ thống có thể tìm thấy dịch vụ của bạn.
Ví dụ này cho thấy cách bạn có thể khai báo dịch vụ trong tệp kê khai ứng dụng:
<service android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
Giờ đây, khi ứng dụng của bạn khai báo dịch vụ, hệ thống có thể tự động khởi động và dừng dịch vụ VPN của ứng dụng khi cần. Ví dụ: hệ thống kiểm soát dịch vụ của bạn khi chạy VPN luôn bật.
Chuẩn bị dịch vụ
Để chuẩn bị cho ứng dụng trở thành dịch vụ VPN hiện tại của người dùng, hãy gọi VpnService.prepare(). Nếu người dùng thiết bị chưa cấp quyền cho ứng dụng của bạn, thì phương thức này sẽ trả về một ý định hoạt động.
Bạn sử dụng ý định này để bắt đầu một hoạt động hệ thống yêu cầu cấp quyền. Hệ thống sẽ hiển thị một hộp thoại tương tự như các hộp thoại cấp quyền khác, chẳng hạn như quyền truy cập vào máy ảnh hoặc danh bạ. Nếu ứng dụng của bạn đã được chuẩn bị, phương thức này sẽ trả về null.
Chỉ một ứng dụng có thể là dịch vụ VPN được chuẩn bị hiện tại. Luôn gọi VpnService.prepare() vì người dùng có thể đã đặt một ứng dụng khác làm dịch vụ VPN kể từ lần gần nhất ứng dụng của bạn gọi phương thức này. Để tìm hiểu thêm, hãy xem phần Vòng đời của dịch vụ.
Kết nối một dịch vụ
Sau khi dịch vụ chạy, bạn có thể thiết lập một giao diện cục bộ mới được kết nối với cổng VPN. Để yêu cầu cấp quyền và kết nối dịch vụ của bạn với cổng VPN, bạn cần hoàn tất các bước theo thứ tự sau:
- Gọi
VpnService.prepare()để xin phép (khi cần). - Gọi
VpnService.protect()để giữ cho cổng kết nối đường hầm của ứng dụng nằm ngoài VPN hệ thống và tránh kết nối vòng tròn. - Gọi
DatagramSocket.connect()để kết nối cổng đường hầm của ứng dụng với cổng VPN. - Gọi các phương thức
VpnService.Builderđể định cấu hình giao diện TUN cục bộ mới trên thiết bị cho lưu lượng truy cập VPN. - Gọi
VpnService.Builder.establish()để hệ thống thiết lập giao diện TUN cục bộ và bắt đầu định tuyến lưu lượng truy cập thông qua giao diện.
Cổng VPN thường đề xuất chế độ cài đặt cho giao diện TUN cục bộ trong quá trình bắt tay. Ứng dụng của bạn gọi các phương thức VpnService.Builder để định cấu hình một dịch vụ như trong mẫu sau:
// Configure a new interface from our VpnService instance. This must be done // from inside a VpnService. val builder = Builder() // Create a local TUN interface using predetermined addresses. In your app, // you typically use values returned from the VPN gateway during handshaking. val localTunnel = builder .addAddress("192.168.2.2", 24) .addRoute("0.0.0.0", 0) .addDnsServer("192.168.1.1") .establish()
Ví dụ trong phần VPN cho mỗi ứng dụng cho thấy một cấu hình IPv6 bao gồm nhiều tuỳ chọn hơn. Bạn cần thêm các giá trị VpnService.Builder sau đây trước khi có thể thiết lập một giao diện mới:
addAddress()- Thêm ít nhất một địa chỉ IPv4 hoặc IPv6 cùng với mặt nạ mạng con mà hệ thống chỉ định làm địa chỉ giao diện TUN cục bộ. Ứng dụng của bạn thường nhận địa chỉ IP và mặt nạ mạng con từ cổng VPN trong quá trình bắt tay.
addRoute()- Thêm ít nhất một tuyến nếu bạn muốn hệ thống gửi lưu lượng truy cập qua giao diện VPN. Lọc tuyến đường theo địa chỉ đích đến. Để chấp nhận tất cả lưu lượng truy cập, hãy đặt một tuyến đường mở như
0.0.0.0/0hoặc::/0.
Phương thức establish() trả về một thực thể ParcelFileDescriptor mà ứng dụng của bạn dùng để đọc và ghi các gói đến và đi từ vùng đệm của giao diện. Phương thức establish()
trả về null nếu ứng dụng của bạn chưa được chuẩn bị hoặc có người thu hồi
quyền.
Vòng đời của dịch vụ
Ứng dụng của bạn phải theo dõi trạng thái của VPN đã chọn trong hệ thống và mọi kết nối đang hoạt động. Cập nhật giao diện người dùng của ứng dụng để người dùng thiết bị biết về mọi thay đổi.
Bắt đầu một dịch vụ
Bạn có thể khởi động dịch vụ VPN theo các cách sau:
- Ứng dụng của bạn khởi động dịch vụ, thường là do người dùng nhấn vào nút kết nối.
- Hệ thống khởi động dịch vụ vì chế độ VPN luôn bật đang bật.
Ứng dụng của bạn bắt đầu dịch vụ VPN bằng cách truyền một ý định đến startService(). Để biết thêm thông tin, hãy xem phần Bắt đầu một dịch vụ.
Hệ thống sẽ khởi động dịch vụ của bạn ở chế độ nền bằng cách gọi onStartCommand(). Tuy nhiên, Android đặt ra các quy định hạn chế đối với
ứng dụng chạy trong nền ở phiên bản 8.0 (cấp độ API 26) trở lên. Nếu hỗ trợ các cấp độ API này, bạn cần chuyển dịch vụ của mình sang nền trước bằng cách gọi Service.startForeground(). Để biết thêm thông tin, hãy xem phần Chạy một dịch vụ trên nền trước.
Dừng dịch vụ
Người dùng thiết bị có thể dừng dịch vụ của bạn bằng cách sử dụng giao diện người dùng của ứng dụng. Dừng dịch vụ thay vì chỉ đóng kết nối. Hệ thống cũng dừng một kết nối đang hoạt động khi người dùng thiết bị thực hiện những thao tác sau trong màn hình VPN của ứng dụng Cài đặt:
- ngắt kết nối hoặc xoá ứng dụng VPN
- tắt VPN luôn bật cho một kết nối đang hoạt động
Hệ thống gọi phương thức onRevoke() của dịch vụ, nhưng lệnh gọi này có thể không xảy ra trên luồng chính. Khi hệ thống gọi phương thức này, một giao diện mạng thay thế đã định tuyến lưu lượng truy cập. Bạn có thể loại bỏ các tài nguyên sau một cách an toàn:
- Đóng cổng kết nối đường hầm được bảo vệ với cổng VPN bằng cách gọi
DatagramSocket.close(). - Đóng chỉ số mô tả tệp bưu kiện (bạn không cần phải xả sạch) bằng cách gọi
ParcelFileDescriptor.close().
VPN luôn bật
Android có thể khởi động dịch vụ VPN khi thiết bị khởi động và duy trì dịch vụ này trong khi thiết bị bật. Tính năng này được gọi là VPN luôn bật và có trong Android 7.0 (cấp độ API 24) trở lên. Mặc dù Android duy trì vòng đời dịch vụ, nhưng dịch vụ VPN của bạn mới là bên chịu trách nhiệm về kết nối cổng VPN. VPN luôn bật cũng có thể chặn các kết nối không sử dụng VPN.
Trải nghiệm người dùng
Trong Android 8.0 trở lên, hệ thống sẽ hiển thị các hộp thoại sau để người dùng thiết bị biết về VPN luôn bật:
- Khi kết nối VPN luôn bật bị ngắt kết nối hoặc không thể kết nối, người dùng sẽ thấy một thông báo không thể đóng. Khi nhấn vào thông báo, bạn sẽ thấy một hộp thoại giải thích thêm. Thông báo sẽ biến mất khi VPN kết nối lại hoặc khi có người tắt tuỳ chọn VPN luôn bật.
- VPN luôn bật cho phép người dùng thiết bị chặn mọi kết nối mạng không sử dụng VPN. Khi bật tuỳ chọn này, ứng dụng Cài đặt sẽ cảnh báo người dùng rằng họ không có kết nối Internet trước khi VPN kết nối. Ứng dụng Cài đặt sẽ nhắc người dùng thiết bị tiếp tục hoặc huỷ.
Vì hệ thống (chứ không phải người dùng) bắt đầu và dừng kết nối luôn bật, nên bạn cần điều chỉnh hành vi và giao diện người dùng của ứng dụng:
- Tắt mọi giao diện người dùng ngắt kết nối vì hệ thống và ứng dụng Cài đặt kiểm soát kết nối.
- Lưu mọi cấu hình giữa mỗi lần khởi động ứng dụng và định cấu hình kết nối bằng các chế độ cài đặt mới nhất. Vì hệ thống khởi động ứng dụng theo yêu cầu, nên người dùng thiết bị có thể không phải lúc nào cũng muốn định cấu hình kết nối.
Bạn cũng có thể sử dụng cấu hình được quản lý để định cấu hình một kết nối. Cấu hình được quản lý giúp quản trị viên CNTT định cấu hình VPN từ xa.
Phát hiện chế độ luôn bật
Android không có API để xác nhận xem hệ thống đã khởi động dịch vụ VPN của bạn hay chưa. Tuy nhiên, khi ứng dụng của bạn gắn cờ bất kỳ phiên bản dịch vụ nào mà ứng dụng đó khởi động, bạn có thể giả định rằng hệ thống đã khởi động các dịch vụ không được gắn cờ cho VPN luôn bật. Ví dụ:
- Tạo một thực thể
Intentđể bắt đầu dịch vụ VPN. - Gắn cờ dịch vụ VPN bằng cách đặt thêm vào ý định.
- Trong phương thức
onStartCommand()của dịch vụ, hãy tìm cờ trong dữ liệu bổ sung của đối sốintent.
Kết nối bị chặn
Người dùng thiết bị (hoặc quản trị viên CNTT) có thể buộc tất cả lưu lượng truy cập phải sử dụng VPN. Hệ thống sẽ chặn mọi lưu lượng truy cập mạng không sử dụng VPN. Người dùng thiết bị có thể tìm thấy nút chuyển Chặn các đường kết nối không qua VPN trong bảng điều khiển tuỳ chọn VPN trong phần Cài đặt.
Chọn không sử dụng chế độ luôn bật
Nếu ứng dụng của bạn không hỗ trợ VPN luôn bật, bạn có thể chọn không sử dụng (trong Android 8.1 (API cấp 27) trở lên) bằng cách đặt siêu dữ liệu dịch vụ SERVICE_META_DATA_SUPPORTS_ALWAYS_ON thành false. Ví dụ về tệp kê khai ứng dụng sau đây cho thấy cách thêm phần tử siêu dữ liệu:
<service android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
<meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
android:value=false/>
</service>
Khi ứng dụng của bạn chọn không sử dụng VPN luôn bật, hệ thống sẽ tắt các thành phần điều khiển trên giao diện người dùng của tuỳ chọn trong phần Cài đặt.
VPN theo ứng dụng
Ứng dụng VPN có thể lọc những ứng dụng cần cài đặt được phép gửi lưu lượng truy cập qua kết nối VPN. Bạn có thể tạo danh sách cho phép hoặc danh sách không cho phép, nhưng không thể tạo cả hai. Nếu bạn không tạo danh sách được phép hoặc không được phép, hệ thống sẽ gửi tất cả lưu lượng truy cập mạng qua VPN.
Ứng dụng VPN của bạn phải đặt danh sách trước khi thiết lập kết nối. Nếu bạn cần thay đổi danh sách, hãy thiết lập một kết nối VPN mới. Bạn phải cài đặt ứng dụng trên thiết bị khi thêm ứng dụng đó vào danh sách.
// The apps that will have access to the VPN. val appPackages = arrayOf( "com.android.chrome", "com.google.android.youtube", "com.example.a.missing.app" ) // Loop through the app packages in the array and confirm that the app is // installed before adding the app to the allowed list. val builder = Builder() for (appPackage in appPackages) { try { packageManager.getPackageInfo(appPackage, 0) builder.addAllowedApplication(appPackage) } catch (e: PackageManager.NameNotFoundException) { // The app isn't installed. } } // Complete the VPN interface config. val localTunnel = builder .addAddress("2001:db8::1", 64) .addRoute("::", 0) .establish()
Ứng dụng được cho phép
Để thêm một ứng dụng vào danh sách cho phép, hãy gọi VpnService.Builder.addAllowedApplication(). Nếu danh sách này có một hoặc nhiều ứng dụng, thì chỉ những ứng dụng trong danh sách mới sử dụng VPN.
Tất cả các ứng dụng khác không có trong danh sách sẽ sử dụng mạng hệ thống như thể VPN không chạy. Khi danh sách ứng dụng được phép trống, tất cả ứng dụng đều sử dụng VPN.
Ứng dụng không được phép
Để thêm một ứng dụng vào danh sách không được phép, hãy gọi VpnService.Builder.addDisallowedApplication().
Các ứng dụng không được phép sẽ sử dụng mạng hệ thống như thể VPN không chạy, còn tất cả các ứng dụng khác sẽ sử dụng VPN.
Bỏ qua VPN
VPN của bạn có thể cho phép các ứng dụng bỏ qua VPN và chọn mạng riêng. Để
bỏ qua VPN, hãy gọi VpnService.Builder.allowBypass() khi
thiết lập giao diện VPN. Bạn không thể thay đổi giá trị này sau khi bắt đầu dịch vụ VPN. Nếu một ứng dụng không liên kết quy trình hoặc cổng kết nối của ứng dụng đó với một mạng cụ thể, thì lưu lượng truy cập mạng của ứng dụng đó sẽ tiếp tục thông qua VPN.
Các ứng dụng liên kết với một mạng cụ thể sẽ không có kết nối khi có người chặn lưu lượng truy cập không đi qua VPN. Để gửi lưu lượng truy cập qua một mạng cụ thể, ứng dụng sẽ gọi các phương thức như ConnectivityManager.bindProcessToNetwork() hoặc Network.bindSocket() trước khi kết nối với cổng kết nối.
Mã mẫu
Dự án nguồn mở Android có một ứng dụng mẫu tên là ToyVPN. Ứng dụng này cho biết cách thiết lập và kết nối một dịch vụ VPN.