คุณแตะแถว VPN ในการตั้งค่า เห็นโปรไฟล์ที่คนอื่นติดตั้งไว้ แล้วเริ่มหาการเรียกที่ใช้เพิ่มโปรไฟล์นั้น มันไม่มีการเรียกแบบนั้น แอปเปิดอุโมงค์ของตัวเองได้ผ่าน VpnService แต่รายการในลิสต์นั้นเป็นของแพลตฟอร์มและของไคลเอนต์ที่สร้างมันขึ้นมา การรู้แน่ชัดว่าเส้นแบ่งอยู่ตรงไหนช่วยประหยัดเวลาหนึ่งสัปดาห์ที่ต้องไล่ตามคลาสที่ซ่อนอยู่

เริ่มจากสิ่งที่หน้าจอการตั้งค่าแสดง

ลิสต์ VPN คือทะเบียนโปรไฟล์ที่ระบบหรือไคลเอนต์ของผู้ผลิตติดตั้งไว้ ได้แก่ PPTP, L2TP/IPsec, IKEv2 และแถวที่แอป OEM หรือ MDM ดันเข้ามา แต่ละแถวมีที่อยู่เซิร์ฟเวอร์ ข้อมูลรับรอง และการใช้งานโปรโตคอลที่อยู่ภายนอกโปรเซสของคุณ เมื่อผู้ใช้แตะเชื่อมต่อตรงนั้น ไคลเอนต์ของแพลตฟอร์มเป็นตัวยกอุโมงค์ ไม่ใช่โค้ดของคุณ นั่นคือเหตุผลที่แถวนั้นอยู่รอดหลังรีบูต และเหตุผลที่การถอนการติดตั้งแอปของคุณไม่เคยลบมัน

จากมุมผู้ใช้ ลิสต์ดูเหมือนพื้นผิว API คือที่ที่การเพิ่ม VPN ควรอยู่ห่างเพียงหนึ่งอินเทนต์ แต่ไม่ใช่ จุดเข้าใช้สาธารณะเดียวที่แตะมันได้คืออินเทนต์การนำทาง และทุกอย่างที่เขียนลงไปเป็นของแพลตฟอร์มหรือคอมโพเนนต์ที่มีสิทธิพิเศษ

ผลในทางปฏิบัติ: ถ้าข้อความช่วยเหลือบอกให้ผู้ใช้เพิ่มโปรไฟล์ในการตั้งค่า แอปของคุณไม่ได้เป็นส่วนหนึ่งของขั้นตอนนั้นเลย

สิ่งที่ VpnService ให้คุณจริง ๆ

VpnService และ VpnService.Builder อธิบายอุโมงค์หนึ่งอันที่เป็นของแอปคุณ คุณเพิ่มที่อยู่ เส้นทาง เซิร์ฟเวอร์ DNS และ MTU จำกัดขอบเขตทราฟฟิกด้วย addAllowedApplication() หรือ addDisallowedApplication() แล้ว establish() จะคืน ParcelFileDescriptor สำหรับอินเทอร์เฟซ TUN จากจุดนั้นแอปอ่านแพ็กเก็ต IP และเขียนคำตอบ ซึ่งหมายความว่าโปรโตคอลเป็นหน้าที่ของคุณที่จะทำหรือฝังเข้ามา: OpenVPN, WireGuard, การรับส่งที่กำหนดเอง

ไม่มีอะไรในลำดับนั้นที่พูดถึง PPTP, L2TP/IPsec หรือ IKEv2 Builder ไม่มีสวิตช์ที่บอกให้ใช้ไคลเอนต์ L2TP ของระบบ และไม่มี callback ที่ให้คุณควบคุมโปรไฟล์ที่แพลตฟอร์มเป็นเจ้าของอยู่แล้ว นี่คือเส้นแบ่งจริง และเป็นเส้นแบ่งของการออกแบบ ไม่ใช่ช่องว่างของสิทธิ์

ดังนั้นแอปเป็นไคลเอนต์ VPN บน Android ได้เต็มที่ เพียงแต่เป็นผู้จัดการของไคลเอนต์ที่มากับโทรศัพท์ไม่ได้

กล่องขออนุญาตไม่ใช่ของประดับ

เพราะอุโมงค์ของคุณดักทราฟฟิกของแอปอื่นได้ ระบบจึงให้ผู้ใช้อนุมัติอย่างชัดเจน prepare() คืน Intent ที่คุณต้องเปิด การอนุมัติผูกกับแพ็กเกจของคุณ และขณะที่อุโมงค์ทำงาน แถบสถานะจะแสดงตัวบ่งชี้ถาวร VPN ที่สองที่ขึ้นมาจะปิดการใช้งานอันแรก เพราะ Android รัน VPN ที่ทำงานอยู่ได้ครั้งละหนึ่ง

กฎหนึ่งเดียวที่ทำงานอยู่นี้ทำให้คนสะดุดในการทดสอบ คุณยกอุโมงค์ขึ้นขณะที่ไคลเอนต์องค์กรเชื่อมต่ออยู่ แล้วสรุปว่าโค้ดของคุณเสีย ทั้งที่แพลตฟอร์มเพียงสลับจากไคลเอนต์อีกตัว การอนุมัติสามารถเพิกถอนได้ภายหลัง และ prepare() อาจคืน null บนอุปกรณ์ที่ไม่มีกิจกรรมการยินยอม ดังนั้น establish() ยังต้องจัดการความล้มเหลว

API ที่ซ่อนอยู่และอินเทนต์การตั้งค่า

คำตอบเก่าชี้ไปที่คลาส com.android.internal และอินเทอร์เฟซ AIDL ที่ไคลเอนต์ในตัวใช้ สิ่งเหล่านี้ไม่อยู่ใน SDK เปลี่ยนระหว่างรุ่นโดยไม่แจ้ง และร้านแอปถือว่าการเข้าถึงนั้นเป็นความเสี่ยง การรูทอุปกรณ์ก็ไม่ได้ทำให้มันเสถียร เพราะ root และการเข้าถึง API ที่ซ่อนอยู่เป็นปัญหาที่ไม่เกี่ยวข้องกัน

ตัวช่วยนำทางที่รองรับคือ Settings.ACTION_VPN_SETTINGS ซึ่งมีค่าเป็น android.net.vpn.SETTINGS แก้ไขให้ได้ค่าก่อนเปิดใช้งาน เพราะบิลด์ OEM บางตัวไม่มีกิจกรรมนั้น และถ้าแก้ไม่ได้ให้กลับไปใช้ขั้นตอนแบบเขียนอธิบาย มันพาผู้ใช้ไปที่หน้าจอและไม่ได้ตั้งค่าอะไร

เส้นทางเจ้าของอุปกรณ์และ MDM คือข้อยกเว้นที่คนมักหมายถึง DevicePolicyManager กำหนดแพ็กเกจ VPN ที่เปิดตลอดเวลาได้ และการกำหนดค่าที่จัดการสามารถโหลดการตั้งค่าไว้ล่วงหน้าให้ไคลเอนต์ที่รองรับได้ แต่นั่นก็ยังไม่ใช่ SDK สาธารณะที่เขียนโปรไฟล์ PPTP

มองการตัดสินใจจากฝั่งผู้ใช้

ถ้าผลิตภัณฑ์ของคุณต้องบรรทุกทราฟฟิก ให้วางแผน VpnService พร้อมสแตกโปรโตคอลจริง และเผื่อเวลาสำหรับขั้นตอนการยินยอมและกฎ VPN หนึ่งเดียว ถ้าเป้าหมายคือเชื่อมต่อกับโปรไฟล์องค์กรที่มีอยู่ งานของคุณคือการแนะนำ: ลิงก์ลึกไปที่หน้าจอการตั้งค่า จัดทำเอกสารฟิลด์ และปล่อยให้ไคลเอนต์ของแพลตฟอร์มทำอุโมงค์ ถ้าแอปเพียงต้องเข้าถึงปลายทางส่วนตัว ก็ไม่จำเป็นต้องมีอุโมงค์ของตัวเองเลย

เขียนการตัดสินใจนั้นก่อนเขียนโค้ด เพราะเส้นทางทั้งสามแทบไม่มีอะไรเหมือนกันหลังจากสปรินต์แรก

การตรวจครั้งสุดท้ายก่อนเริ่ม: ข้อกำหนดบอกว่าแอปของเราเป็นฝ่ายเชื่อมต่อ หรืออุปกรณ์เป็นฝ่ายเชื่อมต่อ อย่างแรกคือ VpnService อย่างที่สองคือโปรไฟล์ที่คุณสร้างไม่ได้ การทำให้ประโยคนั้นถูกต้องถูกกว่าการรีแฟกเตอร์ใด ๆ

บทความที่เกี่ยวข้อง