השירות SystemSuspend

ב-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. רכישת המנעול החסימתי. כך מוודאים ששרשור ההשעיה לא נוגע למונה ההשעיה בזמן ששרשור הראשי מנסה להגדיל או להקטין אותו. כשמפעילים או מסירים את נעילת ההתעוררות, ה-thread main נחסם כשמספר העצירות הגיע לאפס וה-thread suspend מנסה לפעול.
  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.
  • אפשר להעביר קריאה חוזרת לשרשור שאחראי להשעיית המערכת.