آیا گوگل از OTA های A/B در دستگاه های خاصی استفاده کرده است؟
بله. نام تجاری بهروزرسانیهای A/B، بهروزرسانیهای یکپارچه (seamless updates) است. گوشیهای پیکسل و پیکسل XL از اکتبر ۲۰۱۶ با A/B عرضه شدند و همه کرومبوکها از پیادهسازی یکسان update_engine از A/B استفاده میکنند. پیادهسازی کد پلتفرم لازم در اندروید ۷.۱ و بالاتر عمومی است.
چرا OTA های A/B بهتر هستند؟
بهروزرسانیهای OTA A/B هنگام دریافت بهروزرسانیها، تجربه کاربری بهتری را ارائه میدهند. اندازهگیریهای مربوط به بهروزرسانیهای امنیتی ماهانه نشان میدهد که این ویژگی تاکنون موفقیتآمیز بوده است: تا ماه مه ۲۰۱۷، ۹۵٪ از دارندگان پیکسل در مقایسه با ۸۷٪ از کاربران نکسوس، آخرین بهروزرسانی امنیتی را پس از یک ماه اجرا میکنند و کاربران پیکسل زودتر از کاربران نکسوس بهروزرسانی را دریافت میکنند. عدم موفقیت در بهروزرسانی بلوکها در طول OTA دیگر منجر به عدم بوت شدن دستگاه نمیشود. تا زمانی که تصویر سیستم جدید با موفقیت بوت نشود، اندروید توانایی بازگشت به تصویر سیستم عامل قبلی را حفظ میکند.
system_other چیست؟
برنامهها در فایلهای .apk ذخیره میشوند که در واقع آرشیوهای ZIP هستند. هر فایل .apk درون خود یک یا چند فایل .dex حاوی بایتکد قابل حمل Dalvik دارد. یک فایل .odex (.dex بهینه شده) جدا از فایل .apk وجود دارد و میتواند حاوی کد ماشین مخصوص دستگاه باشد. اگر یک فایل .odex در دسترس باشد، اندروید میتواند برنامهها را با سرعت کامپایل شده از قبل اجرا کند، بدون اینکه مجبور باشد هر بار که برنامه اجرا میشود منتظر کامپایل شدن کد باشد. یک فایل .odex کاملاً ضروری نیست: اندروید در واقع میتواند کد .dex را مستقیماً از طریق تفسیر یا کامپایل Just-In-Time (JIT) اجرا کند، اما یک فایل .odex در صورت وجود فضا، بهترین ترکیب سرعت راهاندازی و سرعت زمان اجرا را فراهم میکند.
مثال: برای فایل installed-files.txt از یک گوشی Nexus 6P با اندروید ۷.۱ و حجم کلی تصویر سیستمی ۲۶۲۸MiB (۲۷۵۵۷۹۲۸۳۶ بایت)، تفکیک بزرگترین عوامل مؤثر در حجم کلی تصویر سیستم بر اساس نوع فایل به شرح زیر است:
| .odex | ۱۳۹۱۷۷۰۳۱۲ بایت | ۵۰.۵٪ |
| .apk | ۸۴۶۸۷۸۲۵۹ بایت | ۳۰.۷٪ |
| .so (کد بومی C/C++) | ۲۰۲۱۶۲۴۷۹ بایت | ۷.۳٪ |
| فایلهای .oat/تصاویر .art | ۱۶۳۸۹۲۱۸۸ بایت | ۵.۹٪ |
| فونتها | ۳۸۹۵۲۳۶۱ بایت | ۱.۴٪ |
| دادههای محلی بخش مراقبتهای ویژه | ۲۷۴۶۸۶۸۷ بایت | ۰.۹٪ |
این ارقام برای سایر دستگاهها نیز مشابه است، بنابراین در دستگاههای Nexus/Pixel، فایلهای .odex تقریباً نیمی از پارتیشن سیستم را اشغال میکنند. این بدان معناست که میتوانیم به استفاده از ext4 ادامه دهیم اما فایلهای .odex را در کارخانه در پارتیشن B بنویسیم و سپس در اولین بوت آنها را در /data کپی کنیم. فضای ذخیرهسازی واقعی مورد استفاده با ext4 A/B با SquashFS A/B یکسان است، زیرا اگر از SquashFS استفاده میکردیم، فایلهای .odex از پیش انتخاب شده را به جای system_b در system_a ارسال میکردیم.
آیا کپی کردن فایلهای .odex به /data به این معنی نیست که فضای ذخیره شده در /system در /data از بین میرود؟
دقیقاً نه. در پیکسل، بیشتر فضایی که فایلهای .odex اشغال میکنند، برای برنامهها است که معمولاً در /data وجود دارند. این برنامهها بهروزرسانیهای گوگل پلی را دریافت میکنند، بنابراین فایلهای .apk و .odex موجود در تصویر سیستم برای بیشتر عمر دستگاه بلااستفاده میمانند. چنین فایلهایی را میتوان به طور کامل حذف کرد و با فایلهای .odex کوچک و مبتنی بر پروفایل جایگزین کرد، زمانی که کاربر واقعاً از هر برنامه استفاده میکند (بنابراین به فضایی برای برنامههایی که کاربر استفاده نمیکند نیاز نیست). برای جزئیات بیشتر، به سخنرانی Google I/O 2016 با عنوان «تکامل هنر» مراجعه کنید.
این مقایسه به چند دلیل کلیدی دشوار است:
- برنامههایی که توسط گوگل پلی بهروزرسانی میشوند، به محض دریافت اولین بهروزرسانی، فایلهای .odex خود را در مسیر
/dataداشتهاند. - برنامههایی که کاربر اجرا نمیکند، اصلاً به فایل .odex نیازی ندارند.
- کامپایل مبتنی بر پروفایل، فایلهای .odex کوچکتری نسبت به کامپایل از قبل تولید میکند (زیرا اولی فقط کدهای حیاتی از نظر عملکرد را بهینه میکند).
برای جزئیات بیشتر در مورد گزینههای تنظیم موجود برای تولیدکنندگان اصلی تجهیزات (OEM)، به پیکربندی ART مراجعه کنید.
آیا دو کپی از فایلهای .odex در /data وجود ندارد؟
کمی پیچیدهتر است... پس از نوشتن تصویر سیستم جدید، نسخه جدید dex2oat روی فایلهای .dex جدید اجرا میشود تا فایلهای .odex جدید را تولید کند. این اتفاق زمانی رخ میدهد که سیستم قدیمی هنوز در حال اجرا است، بنابراین فایلهای .odex قدیمی و جدید هر دو به طور همزمان در /data قرار دارند.
کد موجود در OtaDexoptService ( frameworks/base/+/android16-qpr1-release/services/core/java/com/android/server/pm/OtaDexoptService.java ) قبل از بهینهسازی هر بسته، getAvailableSpace را فراخوانی میکند تا از پر شدن بیش از حد /data جلوگیری شود. توجه داشته باشید که available در اینجا هنوز محافظهکارانه است: این مقدار فضای باقیمانده قبل از رسیدن به آستانه فضای کم معمول سیستم است (که هم به صورت درصد و هم به صورت تعداد بایت اندازهگیری میشود). بنابراین اگر /data پر باشد، از هر فایل .odex دو کپی وجود نخواهد داشت. همین کد همچنین دارای یک BULK_DELETE_THRESHOLD است: اگر دستگاه به پر شدن فضای موجود نزدیک شود (همانطور که توضیح داده شد)، فایلهای .odex متعلق به برنامههایی که استفاده نمیشوند حذف میشوند. این مورد دیگری بدون دو کپی از هر فایل .odex است.
در بدترین حالت که /data کاملاً پر شده باشد، بهروزرسانی منتظر میماند تا دستگاه در سیستم جدید راهاندازی مجدد شود و دیگر نیازی به فایلهای .odex سیستم قدیمی نداشته باشد. PackageManager این کار را انجام میدهد: ( frameworks/base/+/android16-qpr1-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215 ). پس از اینکه سیستم جدید با موفقیت بوت شد، installd ( frameworks/native/+/android16-qpr1-release/cmds/installd/dexopt.cpp#2422 ) میتواند فایلهای .odex که توسط سیستم قدیمی استفاده میشدند را حذف کند و دستگاه را به حالت پایدار که فقط یک کپی از آنها وجود دارد، بازگرداند.
بنابراین، اگرچه ممکن است /data شامل دو کپی از تمام فایلهای .odex باشد، (الف) این موقتی است و (ب) فقط در صورتی اتفاق میافتد که در هر صورت فضای خالی زیادی در /data داشته باشید. به جز در هنگام بهروزرسانی، فقط یک کپی وجود دارد. و به عنوان بخشی از ویژگیهای کلی ART، هرگز /data با فایلهای .odex پر نمیکند (زیرا این امر در یک سیستم غیر A/B نیز مشکلساز خواهد بود).
آیا این همه نوشتن/کپی کردن، فرسودگی فلش را افزایش نمیدهد؟
فقط بخش کوچکی از حافظه فلش بازنویسی میشود: یک بهروزرسانی کامل سیستم پیکسل حدود ۲.۳ گیگابایت داده مینویسد. (برنامهها نیز دوباره کامپایل میشوند، اما این در مورد برنامههای غیر A/B نیز صادق است.) به طور سنتی، OTA های کامل مبتنی بر بلوک، مقدار مشابهی داده مینوشتند، بنابراین نرخ فرسودگی حافظه فلش نیز باید مشابه باشد.
آیا فلش کردن دو پارتیشن سیستمی زمان فلش کردن کارخانهای را افزایش میدهد؟
خیر. پیکسل اندازه تصویر سیستم را افزایش نداد (فقط فضا را بین دو پارتیشن تقسیم کرد).
آیا نگه داشتن فایلهای .odex در B باعث کند شدن راهاندازی مجدد پس از تنظیم مجدد کارخانه نمیشود؟
بله. اگر واقعاً از دستگاهی استفاده کردهاید، آن را OTA کردهاید و به تنظیمات کارخانه برگرداندهاید، اولین راهاندازی مجدد کندتر از حالت عادی خواهد بود (۱ دقیقه و ۴۰ ثانیه در مقابل ۴۰ ثانیه در Pixel XL) زیرا فایلهای .odex پس از اولین OTA از B حذف شدهاند و بنابراین نمیتوان آنها را در /data کپی کرد. این بدهبستان است.
بازنشانی به تنظیمات کارخانه در مقایسه با بوت معمولی باید عملی نادر باشد، بنابراین زمان صرف شده اهمیت کمتری دارد. (این موضوع کاربران یا بررسیکنندگانی را که دستگاه خود را از کارخانه دریافت میکنند، تحت تأثیر قرار نمیدهد، زیرا در آن صورت پارتیشن B در دسترس است.) استفاده از کامپایلر JIT به این معنی است که نیازی به کامپایل مجدد همه چیز نداریم، بنابراین آنقدرها هم که فکر میکنید بد نیست. همچنین میتوان برنامهها را با استفاده از coreApp="true" در مانیفست به عنوان برنامههایی که نیاز به کامپایل از قبل دارند، علامتگذاری کرد: ( frameworks/base/+/android16-qpr1-release/packages/SystemUI/AndroidManifest.xml#23 ). این در حال حاضر توسط system_server استفاده میشود زیرا به دلایل امنیتی مجاز به JIT نیست.
آیا نگه داشتن فایلهای .odex در /data به جای /system باعث نمیشود که راهاندازی مجدد بعد از OTA کند شود؟
خیر. همانطور که در بالا توضیح داده شد، dex2oat جدید در حالی اجرا میشود که تصویر سیستم قدیمی هنوز در حال اجرا است تا فایلهایی را که سیستم جدید به آنها نیاز دارد تولید کند. تا زمانی که این کار انجام نشده باشد، بهروزرسانی در دسترس در نظر گرفته نمیشود.
آیا میتوانیم (باید) یک دستگاه A/B با ظرفیت ۳۲ گیگابایت عرضه کنیم؟ ۱۶ گیگابایت؟ ۸ گیگابایت؟
همانطور که روی پیکسل ثابت شد، ۳۲ گیگابایت به خوبی کار میکند و ۳۲۰ مگابایت از ۱۶ گیگابایت به معنای کاهش ۲ درصدی است. به طور مشابه، ۳۲۰ مگابایت از ۸ گیگابایت، کاهش ۴ درصدی را نشان میدهد. بدیهی است که A/B انتخاب توصیه شده برای دستگاههایی با ۴ گیگابایت نخواهد بود، زیرا سربار ۳۲۰ مگابایتی تقریباً ۱۰٪ از کل فضای موجود است.
آیا AVB2.0 به A/B OTA نیاز دارد؟
خیر. بوت تأیید شده اندروید همیشه به بهروزرسانیهای مبتنی بر بلوک نیاز داشته است، اما لزوماً به بهروزرسانیهای A/B نیاز نداشته است.
آیا OTA های A/B به AVB2.0 نیاز دارند؟
خیر.
آیا OTA های A/B محافظت در برابر بازگشت به نسخه قبلی AVB2.0 را از بین میبرند؟
خیر. در اینجا کمی سردرگمی وجود دارد، زیرا اگر یک سیستم A/B نتواند با ایمیج سیستم جدید بوت شود، (پس از تعدادی تلاش مجدد که توسط بوتلودر شما تعیین میشود) به طور خودکار به ایمیج سیستم "قبلی" برمیگردد. نکته کلیدی در اینجا این است که "قبلی" در مفهوم A/B در واقع هنوز ایمیج سیستم "فعلی" است. به محض اینکه دستگاه با موفقیت یک ایمیج جدید را بوت کند، محافظت در برابر بازگشت به نسخه قبلی فعال میشود و تضمین میکند که نمیتوانید به عقب برگردید. اما تا زمانی که ایمیج جدید را با موفقیت بوت نکنید، محافظت در برابر بازگشت به نسخه قبلی آن را به عنوان ایمیج سیستم فعلی در نظر نمیگیرد.
اگر در حین روشن بودن سیستم، بهروزرسانی نصب کنید، سرعت آن پایین نمیآید؟
در بهروزرسانیهای غیر A/B، هدف نصب بهروزرسانی در سریعترین زمان ممکن است، زیرا کاربر منتظر است و نمیتواند در حین اعمال بهروزرسانی از دستگاه خود استفاده کند. در بهروزرسانیهای A/B، عکس این قضیه صادق است؛ زیرا کاربر هنوز از دستگاه خود استفاده میکند، هدف این است که تا حد امکان کمترین تأثیر را داشته باشد، بنابراین بهروزرسانی عمداً کند انجام میشود. اندروید همچنین از طریق منطق موجود در کلاینت بهروزرسانی سیستم جاوا (که برای گوگل GmsCore، بسته اصلی ارائه شده توسط GMS است)، تلاش میکند زمانی را انتخاب کند که کاربران اصلاً از دستگاههای خود استفاده نمیکنند. این پلتفرم از مکث/ازسرگیری بهروزرسانی پشتیبانی میکند و کلاینت میتواند از آن برای مکث بهروزرسانی در صورت شروع استفاده کاربر از دستگاه و از سرگیری آن در صورت غیرفعال شدن دستگاه استفاده کند.
هنگام دریافت OTA دو مرحله وجود دارد که به وضوح در رابط کاربری به صورت مرحله ۱ از ۲ و مرحله ۲ از ۲ در زیر نوار پیشرفت نشان داده شده است. مرحله ۱ مربوط به نوشتن بلوکهای داده است، در حالی که مرحله ۲ مربوط به پیشکامپایل کردن فایلهای .dex است. این دو مرحله از نظر تأثیر بر عملکرد کاملاً متفاوت هستند. مرحله اول ورودی/خروجی ساده است. این مرحله به منابع کمی (RAM، CPU، ورودی/خروجی) نیاز دارد زیرا فقط به آرامی بلوکها را کپی میکند.
مرحله دوم dex2oat را برای پیشکامپایل کردن تصویر سیستم جدید اجرا میکند. بدیهی است که این مرحله محدودیتهای کمتری در مورد الزامات خود دارد زیرا برنامههای واقعی را کامپایل میکند. و بدیهی است که کامپایل یک برنامه بزرگ و پیچیده کار بسیار بیشتری نسبت به یک برنامه کوچک و ساده دارد. در حالی که در مرحله 1 هیچ بلوک دیسکی وجود ندارد که بزرگتر یا پیچیدهتر از سایرین باشد.
این فرآیند مشابه زمانی است که گوگل پلی قبل از نمایش اعلان بهروزرسانی ۵ برنامه، بهروزرسانی یک برنامه را در پسزمینه نصب میکند، همانطور که سالهاست انجام میشود.
اگر کاربری واقعاً منتظر بهروزرسانی باشد، چه میشود؟
پیادهسازی فعلی در GmsCore بین بهروزرسانیهای پسزمینه و بهروزرسانیهای آغاز شده توسط کاربر تمایزی قائل نمیشود، اما ممکن است در آینده این کار را انجام دهد. در مواردی که کاربر صریحاً درخواست نصب بهروزرسانی را داده باشد یا در حال مشاهده صفحه پیشرفت بهروزرسانی باشد، ما کار بهروزرسانی را با این فرض که آنها به طور فعال منتظر اتمام آن هستند، در اولویت قرار خواهیم داد.
اگر بهروزرسانی اعمال نشود، چه اتفاقی میافتد؟
در بهروزرسانیهای غیر A/B، اگر بهروزرسانی اعمال نمیشد، کاربر معمولاً با یک دستگاه غیرقابل استفاده مواجه میشد. تنها استثنا زمانی بود که این خرابی قبل از شروع برنامه رخ میداد (مثلاً به دلیل عدم تأیید بسته). در بهروزرسانیهای A/B، عدم اعمال بهروزرسانی تأثیری بر سیستم در حال اجرا ندارد. بهروزرسانی را میتوان بعداً دوباره امتحان کرد.