A/B (seamless) sistem güncellemeleri

Sorunsuz güncellemeler olarak da bilinen eski A/B sistem güncellemeleri, kablosuz (OTA) güncelleme sırasında diskte çalışır durumda bir önyükleme sisteminin kalmasını sağlar. Bu yaklaşım, güncelleme sonrasında cihazın devre dışı kalma olasılığını azaltır. Bu da onarım ve garanti merkezlerinde daha az cihaz değişimi ve cihazın yeniden flaşlanmasını sağlar. ChromeOS gibi ticari sınıftaki diğer işletim sistemleri de A/B güncellemelerini başarıyla kullanır.

A/B sistem güncellemeleri ve bunların işleyiş şekli hakkında daha fazla bilgi için Bölüm seçimi (slotlar) başlıklı makaleyi inceleyin.

A/B sistem güncellemeleri aşağıdaki avantajları sağlar:

  • OTA güncellemeleri, sistem çalışırken kullanıcıyı kesintiye uğratmadan gerçekleşebilir. Kullanıcılar, OTA güncellemesi sırasında cihazlarını kullanmaya devam edebilir. Güncelleme sırasında tek kesinti, cihazın güncellenmiş disk bölümüne yeniden başlatılmasıdır.
  • Güncelleme sonrası yeniden başlatma işlemi, normal bir yeniden başlatma işleminden daha uzun sürmez.
  • Bir OTA uygulanamazsa (örneğin, kötü bir flaş nedeniyle) kullanıcı bundan etkilenmez. Kullanıcı eski işletim sistemini çalıştırmaya devam eder ve istemcinin güncellemeyi tekrar denemesi serbesttir.
  • OTA güncellemesi uygulanır ancak önyükleme başarısız olursa cihaz eski bölüme yeniden başlatılır ve kullanılabilir durumda kalır. İstemci, güncellemeyi tekrar deneyebilir.
  • Tüm hatalar (G/Ç hataları gibi) yalnızca kullanılmayan bölüm setini etkiler ve yeniden denenebilir. Kullanıcı deneyiminin bozulmasını önlemek için G/Ç yükü kasıtlı olarak düşük olduğundan bu tür hataların oluşma olasılığı da azalır.
  • Güncellemeler A/B cihazlara aktarılabilir. Böylece, paketi yüklemeden önce indirme işlemini yapmanıza gerek kalmaz. Akış, kullanıcının /data veya /cache'te güncelleme paketini depolayacak yeterli boş alana sahip olması gerekmediği anlamına gelir.
  • Önbellek bölümü artık OTA güncelleme paketlerini depolamak için kullanılmadığından, önbellek bölümünün gelecekteki güncellemeler için yeterince büyük olduğundan emin olmanıza gerek yoktur.
  • dm-verity, cihazın bozulmamış bir görüntüyü başlatmasını sağlar. Bir cihaz, bozuk bir OTA veya dm-verity sorunu nedeniyle başlatılamazsa eski bir görüntüyle yeniden başlatılabilir. (Android Doğrulanmış Başlatma için A/B güncellemeleri gerekmez.)

A/B sistem güncellemeleri hakkında

A/B güncellemeleri hem istemcide hem de sistemde değişiklik yapılmasını gerektirir. Ancak OTA paket sunucusunda değişiklik yapılması gerekmez. Güncelleme paketleri hâlâ HTTPS üzerinden sunulur. Google'ın OTA altyapısını kullanan cihazlarda sistem değişikliklerinin tümü AOSP'dedir ve istemci kodu Google Play Hizmetleri tarafından sağlanır. Google'ın OTA altyapısını kullanmayan OEM'ler AOSP sistem kodunu yeniden kullanabilir ancak kendi istemcilerini sağlamalıdır.

Kendi istemcisini sağlayan OEM'lerin istemcinin:

  • Ne zaman güncelleme alacağınıza karar verin. A/B güncellemeleri arka planda gerçekleştiğinden artık kullanıcı tarafından başlatılmaz. Kullanıcıların rahatsız edilmemesi için güncellemelerin, cihaz boşta bakım modundayken (ör. gece) ve kablosuz ağdayken planlanması önerilir. Ancak istemciniz istediğiniz tüm sezgisel kuralları kullanabilir.
  • OTA paket sunucularınızla iletişime geçin ve güncelleme olup olmadığını belirleyin. Bu kod, cihazın A/B'yi desteklediğini belirtmeniz dışında mevcut istemci kodunuzla büyük oranda aynı olmalıdır. (Google'ın istemcisi, kullanıcıların en son güncellemeyi kontrol edebilmesi için Şimdi kontrol et düğmesi de içerir.)
  • Mevcutsa güncelleme paketinizin HTTPS URL'sini kullanarak update_engine'ü çağırın. update_engine, güncelleme paketini yayınlarken şu anda kullanılmayan bölümdeki ham blokları günceller.
  • update_engine sonuç koduna göre yükleme başarılarını veya hatalarını sunucularınıza bildirin. Güncelleme başarıyla uygulanırsa update_engine, önyükleyiciye bir sonraki yeniden başlatmada yeni işletim sistemini başlatmasını söyler. Yeni işletim sistemi başlatılamazsa önyükleyici eski işletim sistemine geri döner. Bu nedenle, istemcinin herhangi bir işlem yapması gerekmez. Güncelleme başarısız olursa istemcinin ayrıntılı hata koduna göre ne zaman (ve tekrar deneyip denemeyeceğine) karar vermesi gerekir. Örneğin, iyi bir istemci, kısmi ("diff") OTA paketinin başarısız olduğunu fark edip bunun yerine tam OTA paketini deneyebilir.

İsteğe bağlı olarak müşteri:

  • Kullanıcıdan yeniden başlatmasını isteyen bir bildirim gösterin. Kullanıcının düzenli olarak güncelleme yapmasının teşvik edildiği bir politika uygulamak istiyorsanız bu bildirim istemcinize eklenebilir. İstemci kullanıcılardan izin istemezse kullanıcılar cihazı yeniden başlattıklarında güncellemeyi alırlar. (Google'ın istemcisinde güncelleme başına yapılandırılabilir bir gecikme vardır.)
  • Kullanıcılara yeni bir işletim sistemi sürümüne mi yoksa eski işletim sistemi sürümüne mi (Google'ın müşterisi genellikle bu ikisini de yapmaz.)

Sistem tarafında A/B sistem güncellemeleri aşağıdakileri etkiler:

  • Bölüm seçimi (yuvalar), update_engine daemon'u ve önyükleme yöneticisi etkileşimleri (aşağıda açıklanmıştır)
  • Derleme işlemi ve OTA güncelleme paketi oluşturma (A/B güncellemelerini uygulama bölümünde açıklanmıştır)

Bölme seçimi (slotlar)

A/B sistem güncellemeleri, slot olarak adlandırılan iki bölüm grubu kullanır (genellikle A ve B yuvası). Sistem mevcut yuvada çalışırken kullanılmayan yuvdaki bölümlere normal çalışma sırasında çalışan sistem tarafından erişilmez. Bu yaklaşım, kullanılmayan yuvayı yedek olarak tutarak güncellemeleri hatalara karşı dirençli hale getirir: Güncelleme sırasında veya hemen sonrasında bir hata meydana gelirse sistem eski yuvaya geri dönebilir ve çalışmaya devam edebilir. Bu hedefe ulaşmak için mevcut yuva tarafından kullanılan hiçbir bölüm, OTA güncellemesi kapsamında güncellenmemelidir (yalnızca bir kopyasının bulunduğu bölümler dahil).

Her yuvanın, cihazın başlatılabileceği doğru bir sistem içerip içermediğini belirten bir bootable özelliği vardır. Sistem çalışırken mevcut yuvadan önyükleme yapılabilir ancak diğer yuva, sistemin eski (yine de doğru) bir sürümüne, daha yeni bir sürüme veya geçersiz verilere sahip olabilir. Mevcut yuva ne olursa olsun, etkin yuva (açılış yükleyicinin bir sonraki açılışta önyükleme yapacağı yuva) veya tercih edilen yuva vardır.

Her yuva, kullanıcı alanı tarafından ayarlanan bir başarılı özelliğine de sahiptir. Bu özellik yalnızca yuva da önyüklenebilirse alakalı olur. Başarılı bir yuva, kendisini başlatabilir, çalıştırabilir ve güncelleyebilmelidir. Başarılı olarak işaretlenmemiş (bu yuvadan önyükleme yapmak için birkaç deneme yapıldıktan sonra) bir önyükleme yuvasının, etkin yuvanın başka bir önyükleme yuvasıyla (normalde yeni, etkin yuvaya önyükleme yapma denemesinden hemen önce çalışan yuva) değiştirilmesi de dahil olmak üzere önyükleme yapılamaz olarak işaretlenmesi gerekir. Arayüzün ayrıntıları boot_control.h bölümünde tanımlanır.

Motor daemon'ını güncelleme

A/B sistem güncellemeleri, sistemi yeni ve güncel bir sürüme önyüklemeye hazırlamak için update_engine adlı bir arka plan hizmetini kullanır. Bu cin aşağıdaki işlemleri gerçekleştirebilir:

  • Mevcut A/B yuvası bölümlerinden veri okuyup OTA paketinin talimatlarına göre kullanılmayan A/B yuvası bölümlerine yazmalıdır.
  • Önceden tanımlanmış bir iş akışında boot_control arayüzünü çağırın.
  • Kullanılmayan tüm yuva bölümlerinin yazılması (Ayrıntılar için Kurulum sonrası bölümüne bakın.)

update_engine daemon'u, önyükleme işleminin kendisinde yer almadığı için güncelleme sırasında yapabileceği işlemler, mevcut yuvada bulunan SELinux politikaları ve özellikleriyle sınırlıdır (bu tür politikalar ve özellikler, sistem yeni bir sürüme önyüklenene kadar güncellenemez). Sağlam bir sistem sağlamak için güncelleme işlemi, bölüm tablosunu, mevcut yuvada bulunan bölümlerin içeriğini veya A/B dışındaki bölümlerin fabrika ayarlarına sıfırlamayla silinemeyen içeriklerini değiştirmemelidir.

Motor kaynağını güncelleme

update_engine kaynağı system/update_engine'dadır. A/B OTA dexopt dosyaları installd ve bir paket yöneticisi arasında bölünür:

Çalışan bir örnek için /device/google/marlin/device-common.mk bölümüne bakın.

Motor günlüklerini güncelleme

Android 8.x ve önceki sürümlerde update_engine günlüklerini logcat'da ve hata raporunda bulabilirsiniz. update_engine günlüklerinin dosya sisteminde kullanılabilmesi için aşağıdaki değişiklikleri derlemenize uygulayın:

Bu değişiklikler, en son update_engine günlüğünün bir kopyasını /data/misc/update_engine_log/update_engine.YEAR-TIME'e kaydeder. Mevcut günlüke ek olarak en son beş günlük /data/misc/update_engine_log/ altında kaydedilir. Günlük grup kimliğine sahip kullanıcılar dosya sistemi günlüklerine erişebilir.

Önyükleme yöneticisi etkileşimleri

boot_control HAL, önyükleyiciye hangi cihazdan önyükleme yapacağını bildirmek için update_engine (ve muhtemelen diğer daemon'lar) tarafından kullanılır. Yaygın örnek senaryolar ve bunlarla ilişkili durumlar şunlardır:

  • Normal durum: Sistem, mevcut A veya B yuvasından çalışıyordur. Henüz güncelleme uygulanmadı. Sistemin mevcut yuvası önyükleme yapılabilir, başarılı ve etkin yuvadır.
  • Güncelleme devam ediyor: Sistem B yuvasından çalışıyorsa B yuvası, önyükleme yapılabilir, başarılı ve etkin yuvadır. A yuvasının içeriği güncellenmekte ancak henüz tamamlanmadığı için A yuvası, önyükleme yapılamaz olarak işaretlendi. Bu durumda yeniden başlatma işlemi, B yuvasından önyüklemeye devam eder.
  • Güncelleme uygulandı, yeniden başlatma bekleniyor: Sistem B yuvasından çalışıyor, B yuvası önyükleme yapılabilir ve başarılı ancak A yuvası etkin olarak işaretlendi (ve bu nedenle önyükleme yapılabilir olarak işaretlendi). A yuvası henüz başarılı olarak işaretlenmemiştir ve önyükleyici tarafından A yuvasından önyükleme yapma girişiminde bulunulmalıdır.
  • Sistem yeni güncellemeye yeniden başlatıldı: Sistem ilk kez A yuvasından çalışıyor, B yuvası hâlâ önyükleme yapabilir ve başarılıdır. A yuvası ise yalnızca önyükleme yapabilir ve hâlâ etkindir ancak başarılı değildir. Kullanıcı alanı daemon'ı (update_verifier), bazı kontroller yapıldıktan sonra A yuvasını başarılı olarak işaretlemelidir.

Akış güncellemesi desteği

Kullanıcı cihazlarında /data'te güncelleme paketini indirmek için her zaman yeterli alan yoktur. OEM'ler de kullanıcılar da /cache bölümünde yer kaybetmek istemediğinden, bazı kullanıcılar güncelleme alamaz. Bunun nedeni, cihazda güncelleme paketinin depolanacağı bir yer olmamasıdır. Bu sorunu gidermek için Android 8.0, blokları /data'te depolamak zorunda kalmadan indirildikleri sırada doğrudan B bölümüne yazan A/B güncellemelerini aktarma desteği ekledi. A/B güncellemelerinin aktarılması için neredeyse hiç geçici depolama alanı gerekmez ve yaklaşık 100 KiB meta veri için yeterli depolama alanı gerekir.

Android 7.1'de akış güncellemelerini etkinleştirmek için aşağıdaki yamaları seçin:

Bu yamalar, Google Mobil Hizmetleri (GMS) veya başka bir güncelleme istemcisi kullanılarak Android 7.1 ve sonraki sürümlerde A/B güncellemelerinin aktarılmasını desteklemek için gereklidir.

A/B güncellemesinin ömrü

Güncelleme işlemi, OTA paketi (kodda yük olarak adlandırılır) indirilebilir hale geldiğinde başlar. Cihazdaki politikalar, yükü indirme ve uygulama işlemlerini pil seviyesine, kullanıcı etkinliğine, şarj durumuna veya diğer politikalara göre erteleyebilir. Ayrıca, güncelleme arka planda çalıştığı için kullanıcılar güncelleme yapıldığını fark etmeyebilir. Tüm bunlar, güncelleme sürecinin politikalar, beklenmedik yeniden başlatmalar veya kullanıcı işlemleri nedeniyle herhangi bir noktada kesintiye uğrayabileceği anlamına gelir.

İsteğe bağlı olarak, OTA paketindeki meta veriler güncellemenin aktarılabileceğini belirtir. Aynı paket, aktarılmayan yükleme için de kullanılabilir. Sunucu, istemciye yayın yaptığını bildirmek için meta verileri kullanabilir. Böylece istemci, OTA'yı update_engine'ye doğru şekilde aktarabilir. Kendi sunucuları ve istemcileri olan cihaz üreticileri, sunucunun güncellemenin akışta olduğunu tanımladığından (veya tüm güncellemelerin akışta olduğunu varsaydığında) ve istemcinin akış için update_engine'ye doğru çağrıyı yaptığından emin olarak akış güncellemelerini etkinleştirebilir. Üreticiler, paketin akış varyantı olması gerçeğini kullanarak istemciye bir işaret gönderebilir ve böylece çerçeve tarafına akış olarak aktarma işlemini tetikleyebilir.

Bir yük kullanılabilir hale geldikten sonra güncelleme süreci aşağıdaki gibidir:

Adım Etkinlikler
1 Mevcut slot (veya "kaynak slot") markBootSuccessful() ile başarılı olarak işaretlenir (henüz işaretlenmemişse).
2 Kullanılmayan yuva (veya "hedef yuva"), setSlotAsUnbootable() işlevi çağrılarak önyükleme yapılamaz olarak işaretlenir. Önyükleme yükleyicinin kullanılmayan yuvaya geri dönmesini ve kısa süre içinde geçersiz verilere sahip olmasını önlemek için güncellemenin başında mevcut yuva her zaman başarılı olarak işaretlenir. Sistem, güncelleme uygulamaya başlayabileceği noktaya ulaştıysa diğer önemli bileşenler bozuk olsa bile (ör. kilitlenme döngüsündeki kullanıcı arayüzü) mevcut alan başarılı olarak işaretlenir. Bu sorunları düzeltmek için yeni yazılım göndermek mümkündür.

Güncelleme yükü, yeni sürüme güncelleme talimatlarını içeren opak bir blob'dur. Güncelleme yükü aşağıdakilerden oluşur:
  • Meta veri. Güncelleme yayının nispeten küçük bir kısmı olan meta veriler, hedef yuvada yeni sürümü oluşturmak ve doğrulamak için gereken işlemlerin listesini içerir. Örneğin, bir işlem belirli bir blob'un sıkıştırmasını kaldırıp hedef bölümdeki belirli bloklara yazabilir veya bir kaynak bölümden okuyabilir, ikili bir yamayı uygulayabilir ve hedef bölümdeki belirli bloklara yazabilir.
  • Ek veriler. Güncelleme yükü büyük oranda işlemlerle ilişkili ek verilerden oluştuğu için bu örneklerde sıkıştırılmış blob veya ikili program yaması yer alır.
3 Yük meta verileri indirilir.
4 Meta verilerde tanımlanan her işlem için sırayla ilişkili veriler (varsa) belleğe indirilir, işlem uygulanır ve ilişkili bellek atılır.
5 Tüm bölümler yeniden okunur ve beklenen karma oluşturma işlemine göre doğrulanır.
6 Yükleme sonrası adım (varsa) çalıştırılır. Herhangi bir adımın yürütülmesi sırasında hata meydana gelirse güncelleme başarısız olur ve muhtemelen farklı bir yükü ile yeniden denenir. Şimdiye kadarki tüm adımlar başarılı olduysa güncelleme başarılı olur ve son adım uygulanır.
7 setActiveBootSlot() çağrılarak kullanılmayan slot etkin olarak işaretlenir. Kullanılmayan yuvanın etkin olarak işaretlenmesi, önyüklemenin tamamlanacağı anlamına gelmez. Başlatıcı (veya sistemin kendisi), başarılı bir durum okumazsa etkin yuvayı geri değiştirebilir.
8 Kurulum sonrası (aşağıda açıklanmıştır), bir programı eski sürümde çalışırken "yeni güncelleme" sürümünden çalıştırmayı içerir. OTA paketinde tanımlanmışsa bu adım zorunludur ve program 0 çıkış koduyla dönmelidir. Aksi takdirde güncelleme başarısız olur.
9 Sistem yeni yuvaya yeterince kadar başarılı bir şekilde önyükleme yaptıktan ve yeniden başlatma sonrası kontrolleri tamamladıktan sonra, mevcut yuva (eski adıyla "hedef yuva") markBootSuccessful() çağrılarak başarılı olarak işaretlenir.

Yükleme sonrası

Yükleme sonrası adımın tanımlandığı her bölüm için update_engine, yeni bölümü belirli bir konuma bağlar ve OTA'da belirtilen programı, bağlı bölüme göre yürütür. Örneğin, yükleme sonrası program sistem bölümünde usr/bin/postinstall olarak tanımlanırsa kullanılmayan yuvada bulunan bu bölüm sabit bir konuma (/postinstall_mount gibi) monte edilir ve /postinstall_mount/usr/bin/postinstall komutu yürütülür.

Yükleme sonrası işlemin başarılı olması için eski çekirdeğin şunları yapabilmesi gerekir:

  • Yeni dosya sistemi biçimini monte edin. Sıkıştırılmış dosya sistemi (ör. SquashFS) kullanılıyorsa kullanılan sıkıştırma algoritması gibi ayrıntılar da dahil olmak üzere eski çekirdekte destek sağlanmadığı sürece dosya sistemi türü değiştirilemez.
  • Yeni bölümün yükleme sonrası program biçimini anlayın. Yürütülebilir ve Bağlanabilir Biçim (ELF) ikili dosyası kullanılıyorsa bu dosya eski çekirdekle uyumlu olmalıdır (ör. mimari 32 bitten 64 bit derlemelere geçiş yaptıysa eski bir 32 bit çekirdekte çalışan 64 bit yeni program). Yükleyiciye (ld) başka yollar kullanması veya statik bir ikili dosya oluşturması söylenmediği sürece kitaplıklar yeni sistem görüntüsünden değil, eski sistem görüntüsünden yüklenir.

Örneğin, eski sistemin kabuk ikili dosyası tarafından yorumlanan bir kabuk komut dosyası (üst kısmında #! işaretçisi bulunan) olarak yükleme sonrası program kullanabilir, ardından daha karmaşık bir ikili yükleme sonrası program yürütmek için yeni ortamdan kitaplık yolları oluşturabilirsiniz. Alternatif olarak, ana sistem bölümündeki dosya sistemi biçiminin geriye dönük uyumluluk sorunları veya ara güncellemeler olmadan güncellenmesini sağlamak için yükleme sonrası adımı özel bir küçük bölümden çalıştırabilirsiniz. Bu, kullanıcıların bir fabrika görüntüsünden doğrudan en son sürüme güncellenmesine olanak tanır.

Yeni yükleme sonrası program, eski sistemde tanımlanan SELinux politikalarıyla sınırlıdır. Bu nedenle, yükleme sonrası adım, belirli bir cihazda tasarım gereği yapılması gereken görevleri veya diğer en iyi çaba görevlerini gerçekleştirmek için uygundur. Yükleme sonrası adım, yeniden başlatmadan önce beklenmedik izinler gerektiren tek seferlik hata düzeltmeleri için uygun değildir.

Seçilen yükleme sonrası program, postinstall SELinux bağlamında çalışır. Yeni sisteme yeniden başlatıldıktan sonra, yeni monte edilen bölümdeki tüm dosyalar, özelliklerinden bağımsız olarak postinstall_file ile etiketlenir. Yeni sistemdeki SELinux özelliklerinde yapılan değişiklikler, yükleme sonrası adımı etkilemez. Yükleme sonrası programın ek izinlere ihtiyacı varsa bu izinler yükleme sonrası bağlama eklenmelidir.

Yeniden başlatma sonrasında

Yeniden başlatıldıktan sonra update_verifier, dm-verity'yi kullanarak bütünlük kontrolünü tetikler. Java hizmetlerinin güvenli bir geri alma işlemini engelleyecek geri alınamaz değişiklikler yapmasını önlemek için bu kontrol zygote'tan önce başlar. Bu işlem sırasında, doğrulanmış başlatma veya dm-verity herhangi bir bozulma tespit ederse bootloader ve çekirdek de yeniden başlatmayı tetikleyebilir. Kontrol tamamlandıktan sonra, update_verifier önyüklemenin başarılı olduğunu işaretler.

update_verifier, yalnızca AOSP kodu kullanıldığında A/B OTA paketine dahil edilen /data/ota_package/care_map.txt'te listelenen blokları okur. GmsCore gibi Java sistem güncelleme istemcisi, care_map.txt dosyasını çıkarır, cihazı yeniden başlatmadan önce erişim iznini ayarlar ve sistem yeni sürüme başarıyla yüklendikten sonra çıkarılan dosyayı siler.