מאפייני HAL של משתמש

בארכיטקטורות קיימות של כלי רכב יש כמה יחידות בקרה אלקטרוניות (ECU) מחוץ למערכת המידע והבידור ששולטות בארגונומיה, כמו הגדרות מושבים והתאמות מראות. בהתאם לארכיטקטורות הנוכחיות של החומרה והחשמל, יחידות בקרה אלקטרוניות רבות מופעלות לפני שהמערכת מבוססת-Android של המידע והבידור מופעלת. יחידות ה-ECU האלה יכולות לתקשר עם מערכת מידע ובידור מבוססת-Android דרך שכבת הפשטת החומרה של הרכב (VHAL).

החל מגרסה 11 של Android, מערכת ההפעלה Android Automotive OS ‏ (AAOS) הציגה קבוצה חדשה של מאפיינים ב-VHAL ליצירה, למעבר, להסרה ולשיוך של אביזרים חיצוניים לצורך זיהוי משתמשים. לדוגמה, המאפיינים החדשים האלה מאפשרים לנהגים לקשור אביזר חיצוני, כמו מפתח פיזי, למשתמש Android. לאחר מכן, כשהנהג מתקרב לרכב, יחידת ה-ECU מתעוררת ומזהה את המפתח האלקטרוני. ה-ECU הזה מציין ל-HAL לאיזה משתמש ב-Android צריך להפעיל את מערכת המולטימדיה, וכך מפחית את הזמן שהנהג צריך להמתין עד שהמשתמש ב-Android ייטען.

הפעלת User HAL

צריך להפעיל באופן מפורש את מאפייני ה-HAL של המשתמש, על ידי וידוא שהנכס android.car.user_hal_enabled של המערכת מוגדר ל-true. (אפשר לעשות זאת בקובץ car.mk, כדי שלא יהיה צורך להגדיר אותו באופן ידני). כדי לבדוק אם user_hal_enabled=true מופעל, מוסיפים את UserHalService:

$ adb shell dumpsys car_service --hal UserHalService|grep enabled
user_hal_enabled=true

אפשר גם לבדוק את user_hal_enabled באמצעות adb shell getprop android.car.user_hal_enabled או adb logcat CarServiceHelper *:s. אם הנכס מושבת, תוצג הודעה כמו זו כשהפונקציה system_server תתחיל:

I CarServiceHelper: Not using User HAL

כדי להפעיל את user_hal_enabled באופן ידני, מגדירים את מאפיין המערכת android.car.user_hal_enabled ומפעילים מחדש את system_server:

$ adb shell setprop android.car.user_hal_enabled true
$ adb shell stop && adb shell start

הפלט של logcat נראה כך:

I CarServiceHelper: User HAL enabled with timeout of 5000ms
D CarServiceHelper: Got result from HAL: OK
I CarServiceHelper: User HAL returned DEFAULT behavior

מאפייני HAL של משתמשים

מאפיינים של מחזור החיים של המשתמש

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

הערה: כשיש תמיכה ב-HAL של משתמשים, צריך להטמיע את כל המאפיינים הבאים.

נכס HAL תיאור
INITIAL_USER_INFO
(קריאה/כתיבה)
מערכת Android מפעילה את המאפיין הזה כדי לקבוע איזה משתמש Android המערכת מפעילה כשמאתחלים את המכשיר או ממשיכים לפעול מ'השעיה ל-RAM' (STR). כשמפעילים אותו, ה-HAL חייב להגיב באחת מהאפשרויות הבאות:
  • התנהגות ברירת המחדל שמוגדרת על ידי Android (מעבר למשתמש האחרון שהיה בשימוש או יצירת משתמש חדש אם זו ההפעלה הראשונה).
  • עוברים למשתמש קיים.
  • יוצרים משתמש חדש (עם המאפיינים האופציונליים של שם, דגלים, מערכת ותרבות וכו') ומעבירים את הפעילות למשתמש החדש.

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

דוגמה: כברירת מחדל, מערכת Android מתחילה במשתמש הפעיל האחרון בזמן ההפעלה. אם מזוהה שלט מפתחות של משתמש אחר, ה-ECU מבטל את מאפיין ה-HAL, ובמהלך ההפעלה מערכת Android עוברת להפעלה של המשתמש שצוין.

SWITCH_USER
(קריאה/כתיבה)
המאפיין הזה נקרא כשמשנים את המשתמש הפעיל בחזית ב-Android. מערכת Android או HAL יכולות להפעיל את הנכס כדי לבקש החלפת משתמש. שלושת תהליכי העבודה הם:
  • מודרני. ההעברה התחילה מ-CarUserManager.
  • דור קודם. המעבר התחיל מ-ActivityManager.
  • רכב. ה-HAL קורא לזה כדי לבקש החלפת משתמש.

בתהליך העבודה המודרני משתמש בגישת התחייבות דו-שלבית כדי להבטיח סנכרון של מערכת Android ושל ה-ECU החיצוני. כשמערכת Android מפעילה את המעבר:

  1. יש לבדוק את תקן HAL כדי לקבוע אם אפשר להחליף את המשתמש.

    פלטפורמת HAL מגיבה באמצעות הפקודה SUCCESS או FAILURE, כדי שמערכת Android תדע אם להמשיך או לא.

  2. משלימים את המעבר של המשתמש ב-Android.

    Android שולח תגובה מסוג ANDROID_POST_SWITCH ל-HAL כדי לציין אם המעבר הצליח או נכשל.

ה-HAL צריך להמתין עד לקבלת התגובה ANDROID_POST_SWITCH כדי לעדכן את המצב שלו לסנכרון של יחידות ה-ECU או לעדכון מאפייני HAL אחרים.

דוגמה: בזמן הנסיעה, הנהג מנסה לעבור בין משתמשי Android בממשק המשתמש של מערכת הבידור. עם זאת, מאחר שההגדרות של מושב הרכב קשורות למשתמש ב-Android, המושב זז במהלך המעבר בין משתמשים. כך, בקרת ה-ECU לא מאשרת את המעבר, ה-HAL מגיבה ככשל ומשתמש Android לא עובר.

תהליך העבודה הקודם הוא שיחה חד-כיוונית שנשלחת אחרי שהמשתמש עובר (כך ש-HAL לא יכול לחסום את המעבר). הוא נקרא רק בזמן האתחול (אחרי המעבר הראשוני של המשתמש) או באפליקציות שמפעילות את ActivityManager.switchUser() במקום את CarUserManager.switchUser(). אפליקציות העזר Settings ו-SystemUI כבר משתמשות באפשרות השנייה, אבל אם יצרן ציוד מקורי מספק אפליקציות הגדרות משלו כדי להחליף משתמשים, יצרני ה-OEM צריכים לשנות את השימוש.

דוגמה: אם אפליקציה משתמשת ב-ActivityManager.switchUser() כדי להחליף משתמשים, נשלחת קריאה חד-כיוונית ל-HAL כדי להודיע על החלפת משתמש.

תהליך העבודה של הרכב מגיע מ-HAL ולא ממערכת Android:

  1. ה-HAL מבקש מעבר משתמש.
  2. המערכת משלימה את החלפת המשתמש ב-Android.
  3. Android שולח תגובה מסוג ANDROID_POST_SWITCH ל-HAL כדי לציין אם המעבר הצליח או נכשל.

דוגמה: בועז השתמש בשלט הרכב של עינת כדי לפתוח את הרכב, וה-HAL השיב לבקשה INITIAL_USER_INFO עם מזהה המשתמש של עינת. לאחר מכן, חיישן ביומטרי של ECU זיהה את הנהג כ-Bob, ולכן User HAL שלח בקשה SWITCH_USER להעברת משתמשים.

CREATE_USER
(קריאה/כתיבה)
המערכת של Android קוראת למאפיין הזה כשיוצרים משתמש חדש ב-Android (באמצעות ה-API ‏CarUserManager.createUser()).

ה-HAL משיב עם SUCCESS או FAILURE. אם התשובה של HAL היא כשל, מערכת Android מסירה את המשתמש.

דוגמה: נהג מקש על סמל בממשק המשתמש של מערכת הבידור כדי ליצור משתמש חדש ב-Android. הבקשה נשלחת ל-HAL ולשאר המערכות המשניות ברכב. יחידות ה-ECU מקבלות הודעה על המשתמש החדש שנוצר. לאחר מכן, מערכות משנה ו-ECU אחרים משייכים את מזהה המשתמש הפנימי שלהם למזהה המשתמש ב-Android.

REMOVE_USER
(כתיבה בלבד)
מערכת Android קוראת למאפיין הזה אחרי שמשתמש ב-Android מבוטל (באמצעות השיטה CarUserManager.removeUser()).

זוהי קריאה חד-כיוונית, ולא צפויה תגובה מ-HAL.

דוגמה: נהג מקייש כדי להסיר משתמש Android קיים בממשק המשתמש של מערכת הבידור ברכב. ה-HAL מודיע על הסרת המשתמש, וכן מערכות משנה אחרות ברכב ומערכות בקרה אלקטרוניות (ECU) כדי שיוכלו להסיר את מזהה המשתמש הפנימי שלו.

נכסים נוספים

אלה מאפיינים נוספים שלא קשורים למצבים במחזור החיים של המשתמש. אפשר להטמיע כל אחת מהן בלי לתמוך ב-HAL של המשתמש.

נכס HAL תיאור
USER_IDENTIFICATION_ASSOCIATION
(קריאה/כתיבה)
אפשר להשתמש במאפיין הזה כדי לשייך כל משתמש ב-Android למנגנון זיהוי, כמו שלט מפתחות או טלפון. צריך להשתמש בנכס הזה כדי ליצור שיוכים מסוג get או set.

דוגמה: נהג מקשקש על סמל בממשק המשתמש של מערכת המולטימדיה כדי לשייך את השלט הרחוק המשמש לפתיחת הרכב (KEY_123) למשתמש הפעיל הנוכחי ב-Android‏ (USER_11).

ספריות עזר

לכל האובייקטים שמופיעים בהודעות הבקשה ובתשובה (כמו UserInfo, InitialUserInfoRequest, InitialUSerInfoResponse וכן הלאה) יש ייצוג ברמה גבוהה באמצעות struct+C, אבל ההסרה צריכה להיות שטוח לאובייקטים רגילים VehiclePropValue (ראו דוגמאות בהמשך). כדי להקל על הפיתוח, ב-AOSP יש ספריית עזר ב-C++‎ שממירה באופן אוטומטי את User HAL‏ structs ל-VehiclePropValue (ולהפך).

דוגמאות

INITIAL_USER_INFO

דוגמה לבקשה (באתחול הראשון)

VehiclePropValue { // flattened from InitialUserInfoRequest
prop: 299896583 // INITIAL_USER_INFO
prop.values.int32Values:
 [0] = 1 // Request ID
 [1] = 1 // InitialUserInfoRequestType.FIRST_BOOT
 [2] = 0 // user id of current user
 [3] = 1 // flags of current user (SYSTEM)
 [4] = 1 // number of existing users
 [5] = 0 // existingUser[0].id
 [6] = 1 // existingUser[0].flags
}

דוגמה לתשובה (יצירת משתמש עם הרשאת אדמין)

VehiclePropValue { // flattened from InitialUserInfoResponse
prop: 299896583 // INITIAL_USER_INFO
prop.values.int32Values:
  [0] = 1      // Request ID (must match request)
  [1] = 2      // InitialUserInfoResponseAction.CREATE
  [2] = -10000 // user id (not used on CREATE)
  [3] = 8      // user flags (ADMIN)
prop.values.stringValue: "en-US||Car Owner" // User locale and user name
}

SWITCH_USER

השם בפועל של הכיתות והמאפיינים שונה במקצת, אבל תהליך העבודה הכללי זהה, כפי שמוצג באיור:

זרימת עבודה

איור 1. תהליך העבודה של מאפייני HAL של משתמשים.

דוגמה לבקשה למודרניזציה של תהליך עבודה

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896585 // SWITCH_USER
prop.values.int32Values:
 [0]     = 42    // Request ID
 [1]     = 2     // SwitchUserMessageType::ANDROID_SWITCH ("modern")
 [2,3]   = 11,0  // target user id (11) and flags (none in this case)
 [4,5]   = 10,8  // current user id (10) and flags (ADMIN)
 [6]     = 3     // number of existing users (0, 10, 11)
 [7,8]   = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [9,10]  = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [11,12] = 11,0  // existingUser[2] (id=11, flags=NONE)
}

דוגמה לתגובה של תהליך עבודה מודרני

VehiclePropValue { // flattened from SwitchUserResponse
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0] = 42        // Request ID (must match request)
 [1] = 3         // SwitchUserMessageType::VEHICLE_RESPONSE
 [2] = 1         // SwitchUserStatus::SUCCESS
}

דוגמה לתגובה בתהליך עבודה מודרני אחרי המעבר

התגובה הזו מתקבלת בדרך כלל כשמתבצע מעבר מוצלח ב-Android:

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0]     = 42    // Request ID (must match "pre"-SWITCH_USER request )
 [1]     = 5     // SwitchUserMessageType::ANDROID_POST_SWITCH
 [2,3]   = 11,0  // target user id (11) and flags (none in this case)
 [4,5]   = 11,0  // current user id (11) and flags (none in this case)
 [6]     = 3     // number of existing users (0, 10, 11)
 [7,8]   = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [9,10]  = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [11,12] = 11,0  // existingUser[2] (id=11, flags=NONE)
}

תגובה מודרנית לתהליך עבודה לאחר המעבר

התגובה הזו מתקבלת בדרך כלל כשמתרחשת אחת מהבעיות הבאות במתג Android:

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0]     = 42    // Request ID (must match "pre"-SWITCH_USER request )
 [1]     = 5     // SwitchUserMessageType::ANDROID_POST_SWITCH
 [2,3]   = 11,0  // target user id (11) and flags (none in this case)
 [4,5]   = 10,8  // current user id (10) and flags (ADMIN)
 [6]     = 3     // number of existing users (0, 10, 11)
 [7,8]   = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [9,10]  = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [11,12] = 11,0  // existingUser[2] (id=11, flags=NONE)
}

דוגמה לבקשה לתהליך עבודה מדור קודם

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0]     = 2     // Request ID
 [1]     = 1     // SwitchUserMessageType::LEGACY_ANDROID_SWITCH
 [2,3]   = 10,8  // target user id (10) and flags (ADMIN)
 [4,5]   = 0,1   // current user id (0) and flags (SYSTEM)
 [6]     = 3     // number of existing users (0, 10, 11)
 [7,8]   = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [9,10]  = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [11,12] = 11,0  // existingUser[2] (id=11, flags=NONE)
}

דוגמה לבקשה לגבי תהליך עבודה של רכב

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0]     = -108  // Request ID (must be negative)
 [1]     = 4     // SwitchUserMessageType::VEHICLE_REQUEST
 [2]     = 11    // target user id
}

תגובה של תהליך עבודה מדור קודם אחרי המעבר

התגובה הזו בדרך כלל מתרחשת כשמתג Android מצליח:

VehiclePropValue { // flattened from SwitchUserRequest
prop: 299896584 // SWITCH_USER
prop.values.int32Values:
 [0]     = -108  // Request ID (must match from vehicle request )
 [1]     = 5     // SwitchUserMessageType::ANDROID_POST_SWITCH
 [2,3]   = 11,0  // target user id (11) and flags (none in this case)
 [4,5]   = 11,0  // current user id (11) and flags (none in this case)
 [6]     = 3     // number of existing users (0, 10, 11)
 [7,8]   = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [9,10]  = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [11,12] = 11,0  // existingUser[2] (id=11, flags=NONE)
}

CREATE_USER

דוגמה לבקשה

VehiclePropValue { // flattened from CreateUserRequest
prop: 299896585 // CREATE_USER
prop.values.int32Values:
 [0]      = 42  // Request ID
 [1,2]    = 11,6     // Android id of the created user and flags (id=11, flags=GUEST, EPHEMERAL)
 [3,4]    = 10,0  // current user id (10) and flags (none in this case)
 [5]      = 3  // number of existing users (0, 10, 11)
 [6,7]    = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [8,9]    = 10,8  // existingUser[1] (id=10, flags=ADMIN)
 [10,11] = 11,6 // newUser[2] (id=11, flags=GUEST,EPHEMERAL)
}

דוגמה לתגובה

VehiclePropValue { // flattened from CreateUserResponse
prop: 299896585 // CREATE_USER
prop.values.int32Values:
 [0] = 42        // Request ID (must match request)
 [1] = 3         // CreateUserStatus::SUCCESS
}

REMOVE_USER

דוגמה לבקשה

VehiclePropValue { // flattened from RemoveUserRequest
prop: 299896586 // REMOVE_USER
prop.values.int32Values:
 [0]      = 42  // Request ID
 [1,2]    = 11,0     // Android id of the removed user and flags (none in this case)
 [3,4]    = 10,0  // current user id (10) and flags (none in this case)
 [5]      = 2  // number of existing users (0, 10)
 [6,7]    = 0,1   // existingUser[0] (id=0, flags=SYSTEM)
 [8,9]    = 10,8  // existingUser[1] (id=10, flags=ADMIN)
}

USER_IDENTIFICATION_ASSOCIATION

דוגמה להגדרה (מפתחות רכב שמשויך למשתמש 10)

VehiclePropValue { // flattened from UserIdentificationSetRequest
prop: 299896587 // USER_IDENTIFICATION_ASSOCIATION
prop.values.int32Values:
 [0]      = 43  // Request ID
 [1,2]    = 10,0     // Android id (10) and flags (none in this case)
 [3]    = 1  // number of associations being set
 [4]      = 1  // 1st type: UserIdentificationAssociationType::KEY_FOB
 [5]    = 1   // 1st value: UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER
}