אתם מקישים על שורת ה-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 של המערכת, ואין קריאה חוזרת שמאפשרת לכם להניע פרופיל שהפלטפורמה כבר מחזיקה בו. זה הגבול האמיתי, והוא גבול תכנון ולא פער בהרשאות.

אז אפליקציה בהחלט יכולה להיות לקוח VPN באנדרואיד. היא רק לא יכולה להיות מנהל ללקוחות שמגיעים עם הטלפון.

דיאלוג ההסכמה אינו קישוט

מכיוון שהמנהרה שלכם יכולה ללכוד תעבורה של אפליקציות אחרות, המערכת מחייבת את המשתמש לאשר אותה במפורש. prepare() מחזיר Intent שעליכם להפעיל, האישור קשור לחבילה שלכם, וכל עוד המנהרה פעילה שורת המצב מציגה מחוון קבוע. VPN שני שעולה מנטרל את הראשון, כי אנדרואיד מריץ VPN פעיל אחד בכל פעם.

כלל הפעיל האחד הזה מכשיל אנשים בבדיקות. אתם מעלים את המנהרה שלכם בזמן שלקוח ארגוני מחובר ומסיקים שהקוד שלכם שבור, בעוד הפלטפורמה פשוט עברה מהלקוח האחר. אפשר לבטל אישורים מאוחר יותר, ו-prepare() יכול להחזיר null במכשירים שבהם פעילות ההסכמה חסרה, כך ש-establish() עדיין חייב לטפל בכשל.

ממשקי API נסתרים וכונות הגדרות

תשובות ישנות יותר מפנות למחלקות com.android.internal ולממשקי AIDL שהלקוח המובנה משתמש בהם. הם לא ב-SDK, הם משתנים בין גרסאות בלי הודעה, וחנויות אפליקציות רואות בגישה הזאת סיכון. גם רוט של המכשיר לא הופך אותם ליציבים, כי רוט וגישה ל-API נסתרים הם בעיות לא קשורות.

עזרת הניווט הנתמכת היא Settings.ACTION_VPN_SETTINGS, שערכה android.net.vpn.SETTINGS. פתרו אותה לפני ההפעלה, כי כמה גרסאות OEM לא מספקות את הפעילות הזאת, ועברו לשלבים כתובים כשהפתרון נכשל. היא מובילה את המשתמש למסך ולא מגדירה דבר.

מסלולי בעלים של מכשיר ו-MDM הם החריג שאנשים בדרך כלל מתכוונים אליו. DevicePolicyManager יכול לקבוע חבילת VPN שפועלת תמיד, ותצורות מנוהלות יכולות לטעון מראש הגדרות ללקוח שתומך בהן, אבל זה עדיין לא ה-SDK הציבורי שמחבר פרופיל PPTP.

עברו על ההחלטה מצד המשתמש

אם המוצר שלכם צריך לשאת תעבורה, תכננו VpnService ועליו מחסנית פרוטוקולים אמיתית, והקצו זמן לזרימת ההסכמה ולכלל ה-VPN האחד. אם המטרה היא להתחבר לפרופיל ארגוני קיים, התפקיד שלכם הוא הדרכה: קישור עמוק למסך ההגדרות, תיעוד השדות, ותנו ללקוח הפלטפורמה לעשות את המנהור. אם האפליקציה רק צריכה להגיע לנקודת קצה פרטית, היא בכלל לא צריכה מנהרה משל עצמה.

רשמו את ההחלטה הזאת לפני שאתם כותבים קוד, כי לשלושת המסלולים אין כמעט דבר משותף אחרי הספרינט הראשון.

בדיקה אחרונה לפני שמתחילים: האם הדרישה אומרת שהאפליקציה שלנו מתחברת, או שהמכשיר מתחבר? הראשון הוא VpnService, השני הוא פרופיל שאינכם יכולים ליצור. לנסח את המשפט הזה נכון זול יותר מכל ריפקטור.

מאמרים קשורים