שירות Systemהשעיה של שירות

ב-Android 9 וגרסאות קודמות, יש שרשור ב-libsuspend שאחראי להפעלת השהיה של המערכת. ב-Android 10 נוספה פונקציונליות מקבילה בשירות SystemSuspend HIDL. השירות הזה נמצא בתמונת המערכת ומופעל על ידי פלטפורמת Android. הלוגיקה מ-libsuspend נשארת ללא שינוי, מלבד העובדה שכל תהליך במרחב המשתמש שחוסם את השהיה המערכת צריך לתקשר עם SystemSuspend.

libsuspend ו-libpower

ב-Android 10, השירות SystemSuspend מחליף את libsuspend. libpower הוטמע מחדש כך שיסתמך על השירות SystemSuspend במקום על /sys/power/wake[un]lock, בלי לשנות את ה-API ל-C.

הקוד המדומה הזה מראה איך מטמיעים את acquire_wake_lock ואת release_wake_lock.


static std::unordered_map<std::string, sp<IWakeLock>> gWakeLockMap;

int acquire_wake_lock(int, const char* id) {
    ...
    if (!gWakeLockMap[id]) {
        gWakeLockMap[id] = suspendService->acquireWakeLock(WakeLockType::PARTIAL, id);
    }
    ...
    return 0;
}

int release_wake_lock(const char* id) {
    ...
    if (gWakeLockMap[id]) {
        auto ret = gWakeLockMap[id]->release();
        gWakeLockMap[id].clear();
        return 0;
    }
    ...
    return -1;
}

שרשורי ביצוע

שירות SystemSuspend עוקב אחרי מספר נעילת ההתעוררות שהונפקו באמצעות מונה השהיה. יש לו שני חוטי ביצוע:

  • ה-thread הראשי משיב להפעלות של Binder.
  • השרשור suspend קובע אם המערכת תושעה.

Thread ראשי

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

השעיית השרשור

שרשור ההשהיה מבצע את הפעולות הבאות בלולאה:

  1. קוראים את /sys/power/wakeup_count.
  2. רכישת המנעול החסימתי. כך מוודאים ששרשור ההשעיה לא נוגע למונה ההשעיה בזמן ששרשור הראשי מנסה להגדיל או להקטין אותו. כשמספר העצירות ב-suspend counter מגיע לאפס וה-thread‏ suspend מנסה לפעול, ה-thread‏ main נחסם במהלך הניסיון להוסיף או להסיר את נעילת ההתעוררות.
  3. ממתינים עד שהמספר בספירה יהיה אפס.
  4. כותבים בקובץ הזה את הערך שקראו מ-/sys/power /wakeup_count (משלב 1). אם הכתיבה נכשלת, חוזרים לתחילת הלולאה
  5. כדי להתחיל את השהיה המערכת, כותבים mem ב-/sys/power/state.
  6. משחררים את מנעול ה-mutex.

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

איור 1. השהיה של לולאת השרשור

SystemSuspend API

SystemSuspend API מורכב משני ממשקים. תהליכים מקומיים משתמשים בממשק HIDL כדי לקבל מנעולי התעוררות, וממשק AIDL משמש לתקשורת בין SystemServer לבין SystemSuspend.

ממשק HIDL של ISystemSuspend


enum WakeLockType : uint32_t {
    PARTIAL,
    FULL
};

interface IWakeLock {
    oneway release();
};

interface ISystemSuspend {
    acquireWakeLock(WakeLockType type, string debugName)
        generates (IWakeLock lock);
};

כל לקוח שמבקש נעילת התעוררות מקבל מכונה ייחודית של IWakeLock. זה שונה מ-/sys/power/wake_lock, שמאפשר לכמה לקוחות להשתמש במנעול ההתעוררות עם אותו שם. אם לקוח שמחזיק במכונה של IWakeLock מסתיים, מנהל ה-binder והשירות SystemSuspend מנקים אותו.

ממשק AIDL של ISuspendControlService

השירות ISuspendControlService מיועד לשימוש רק על ידי SystemServer.


interface ISuspendCallback {
     void notifyWakeup(boolean success);
}

interface ISuspendControlService {
    boolean enableAutosuspend();
    boolean registerCallback(ISuspendCallback callback);
    boolean forceSuspend();
}

היתרונות של שימוש ב-Android HIDL:

  • אם תהליך שחוסם השהיה יסתיים, ניתן יהיה להודיע ל-SystemSuspend.
  • אפשר להעביר קריאה חוזרת לשרשור שאחראי להשעיית המערכת.