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

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

libightend ו-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.
  • השעיה של מערכת ההשעיה של אמצעי הבקרה לשרשורים.

Thread ראשי

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

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

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

  1. קוראים את /sys/power/wakeup_count.
  2. רכישת ה-mutex. כך מוודאים ששרשור ההשעיה לא נוגע למונה ההשעיה בזמן ששרשור הראשי מנסה להגדיל או להקטין אותו. כשמספר העצירות ב-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 מסתיים, מנהל ההתקן של הקישור והשירות System suspended מנקים אותו.

ממשק AIDL של ISuspendControlService

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


interface ISuspendCallback {
     void notifyWakeup(boolean success);
}

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

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

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