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ışabilir bir başlatma sisteminin kalmasını sağlar. Bu yaklaşım, güncelleme sonrasında cihazın etkin olmama olasılığını azaltır. Bu da onarım ve garanti merkezlerinde daha az cihaz değişimi ve cihaz yeniden başlatma anlamına gelir. ChromeOS gibi diğer ticari sınıf işletim sistemlerinde de A/B güncellemeleri başarıyla kullanılı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 yapılabilir. Kullanıcılar, OTA sırasında cihazlarını kullanmaya devam edebilir. Güncelleme sırasında tek kesinti, cihaz güncellenen disk bölümüne yeniden başlatıldığında yaşanır.
  • Güncellemeden sonra yeniden başlatma işlemi normal yeniden başlatma işleminden daha uzun sürmez.
  • Bir OTA uygulanamazsa (ör. kötü bir flash nedeniyle) kullanıcı etkilenmez. Kullanıcı eski işletim sistemini çalıştırmaya devam eder ve istemci, güncellemeyi yeniden denemekte serbesttir.
  • Bir OTA güncellemesi uygulanır ancak başlatma işlemi başarısız olursa cihaz eski bölüme yeniden başlatılır ve kullanılabilir durumda kalır. İstemci, güncellemeyi yeniden denemekte serbesttir.
  • Tüm hatalar (ör. G/Ç hataları) yalnızca kullanılmayan bölüm kümesini etkiler ve yeniden denenebilir. Kullanıcı deneyimini kötüleştirmemek için G/Ç yükü kasıtlı olarak düşük tutulduğundan bu tür hataların oluşma olasılığı da azalır.
  • Güncellemeler, A/B cihazlara aktarılabilir. Bu sayede, paketin yüklenmeden önce indirilmesi gerekmez. Yayınlama, kullanıcının güncelleme paketini /data veya /cache üzerinde depolamak için yeterli boş alana sahip olmasını gerektirmez.
  • Ö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 olmasını sağlamanıza gerek yoktur.
  • dm-verity bir cihazın bozulmamış bir görüntüyle başlatılacağını garanti eder. Bir cihaz, hatalı OTA veya dm-verity sorunu nedeniyle başlatılamıyorsa eski bir görüntüyle yeniden başlatılabilir. (Android'de Doğrulanmış Başlatma için A/B güncellemeleri gerekmez.)

A/B sistem güncellemeleri hakkında

A/B güncellemeleri için hem istemcide hem de sistemde değişiklik yapılması gerekir. Ancak OTA paketi sunucusunda değişiklik yapılması gerekmez: Güncelleme paketleri HTTPS üzerinden sunulmaya devam eder. Google'ın OTA altyapısını kullanan cihazlarda sistem değişikliklerinin tamamı AOSP'de yapılır 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ğlamaları gerekir.

Kendi istemcisini sağlayan OEM'lerin istemcisi şunları yapmalıdır:

  • Güncellemeyi ne zaman yapacağı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 kesintiye uğramaması için güncellemelerin, cihazın boşta bakım modunda olduğu zamanlarda (ör. gece) ve kablosuz ağa bağlıyken planlanması önerilir. Ancak istemciniz istediğiniz sezgisel yöntemleri kullanabilir.
  • OTA paketi sunucularınızla iletişime geçin ve güncelleme olup olmadığını belirleyin. Bu, cihazın A/B'yi desteklediğini belirtmeniz dışında mevcut istemci kodunuzla büyük ölçüde aynı olmalıdır. (Google'ın istemcisinde, kullanıcıların en son güncellemeyi kontrol etmesi için Şimdi kontrol et düğmesi de bulunur.)
  • Varsa güncelleme paketinizin HTTPS URL'siyle update_engine işlevini çağırın. update_engine, güncelleme paketini yayınlarken şu anda kullanılmayan bölümde 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, bir sonraki yeniden başlatmada önyükleyiciye 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, istemci tarafından herhangi bir işlem yapılması gerekmez. Güncelleme başarısız olursa istemcinin, ayrıntılı hata koduna göre ne zaman (ve tekrar) deneyeceğine karar vermesi gerekir. Örneğin, iyi bir istemci, kısmi ("diff") OTA paketinin başarısız olduğunu anlayıp bunun yerine tam OTA paketini deneyebilir.

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

  • Kullanıcıdan yeniden başlatmasını isteyen bir bildirim gösterin. Kullanıcının düzenli olarak güncellemeye teşvik edildiği bir politika uygulamak istiyorsanız bu bildirimi istemcinize ekleyebilirsiniz. İstemci, kullanıcılardan istemde bulunmazsa kullanıcılar güncellemeyi bir sonraki yeniden başlatma işleminde yine de alır. (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 önyükleme yapıp yapmadıklarını veya yapmaları beklenirken eski işletim sistemi sürümüne geri dönüp dönmediklerini bildiren bir bildirim gösterin. (Google'ın müşterisi genellikle ikisini de yapmaz.)

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

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

Bölüm seçimi (yuvalar)

A/B sistem güncellemelerinde yuvalar (genellikle yuva A ve yuva B) olarak adlandırılan iki bölüm kümesi kullanılır. Sistem, geçerli yuvadan çalışır. Kullanılmayan yuvadaki bölümlere ise normal çalışma sırasında çalışan sistem tarafından erişilmez. Bu yaklaşım, kullanılmayan yuvanın yedek olarak kalmasını sağlayarak güncellemeleri hataya karşı dirençli hale getirir: Güncelleme sırasında veya hemen sonrasında bir hata oluşursa sistem eski yuvaya geri dönebilir ve çalışmaya devam edebilir. Bu hedefe ulaşmak için OTA güncellemesi kapsamında mevcut yuva tarafından kullanılan hiçbir bölüm güncellenmemelidir (yalnızca bir kopyası olan bölümler dahil).

Her yuvada, yuvanın cihazın başlatılabileceği doğru bir sistem içerip içermediğini belirten bir başlatılabilir özelliği bulunur. Sistem çalışırken mevcut yuva başlatılabilir ancak diğer yuvada sistemin eski (hâlâ doğru) bir sürümü, daha yeni bir sürüm veya geçersiz veriler olabilir. Geçerli yuva ne olursa olsun, etkin yuva (bir sonraki başlatmada önyükleyicinin başlatacağı yuva) veya tercih edilen yuva olmak üzere bir yuva vardır.

Her yuvada, kullanıcı alanı tarafından ayarlanan bir başarılı özelliği de bulunur. Bu özellik yalnızca yuva başlatılabilirse geçerlidir. Başarılı bir yuva, başlatılabilir, çalıştırılabilir ve kendini güncelleyebilir. Başarılı olarak işaretlenmemiş (kendisiyle başlatma işlemi birkaç kez denenmiş olmasına rağmen) başlatılabilir bir yuva, başlatıcı tarafından başlatılamaz olarak işaretlenmelidir. Bu işlem, etkin yuvanın başka bir başlatılabilir yuvaya (normalde yeni ve etkin yuvaya başlatma girişimi yapılmadan hemen önce çalışan yuvaya) değiştirilmesini de içerir. Arayüzün ayrıntıları boot_control.h bölümünde tanımlanır.

Update Engine Daemon

A/B sistem güncellemeleri, sistemi yeni ve güncellenmiş bir sürümde başlatmaya hazırlamak için update_engine adlı bir arka plan hizmetini kullanır. Bu daemon aşağıdaki işlemleri yapabilir:

  • Mevcut yuva A/B bölümlerinden okuma yapın ve OTA paketi tarafından belirtildiği şekilde kullanılmayan yuva A/B bölümlerine veri yazın.
  • Önceden tanımlanmış bir iş akışında boot_control arayüzünü çağırın.
  • OTA paketi tarafından belirtildiği şekilde, kullanılmayan tüm yuva bölümleri yazıldıktan sonra yeni bölümden yükleme sonrası bir program çalıştırın. (Ayrıntılar için Kurulum sonrası bölümüne bakın).

update_engine daemon, başlatma sürecine dahil olmadığından güncelleme sırasında yapabilecekleri, SELinux politikaları ve mevcut yuvadaki özellikleriyle sınırlıdır (bu tür politikalar ve özellikler, sistem yeni bir sürümde başlatılana kadar güncellenemez). Güncelleme işlemi, sağlam bir sistemi korumak için bölüm tablosunu, mevcut yuvadaki bölümlerin içeriklerini veya fabrika ayarlarına sıfırlama ile silinemeyen A/B olmayan bölümlerin içeriklerini değiştirmemelidir.

Güncelleme motoru kaynağı

update_engine kaynağı system/update_engine konumunda bulunur. A/B OTA dexopt dosyaları installd ile 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 sürümleri ve daha eski sürümlerde update_engine günlükleri logcat ve hata raporunda bulunabilir. 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 konumuna kaydeder. Geçerli günlüğe ek olarak, en son beş günlük /data/misc/update_engine_log/ altında kaydedilir. Günlük grubu kimliğine sahip kullanıcılar, dosya sistemi günlüklerine erişebilir.

Bootloader etkileşimleri

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

  • Normal durum: Sistem, A veya B yuvası olmak üzere mevcut yuvasından çalışır. Şu ana kadar herhangi bir güncelleme uygulanmadı. Sistemin mevcut yuvası başlatılabilir, başarılı ve etkin yuvadır.
  • Güncelleme devam ediyor: Sistem B yuvasından çalışıyor. Bu nedenle B yuvası, önyüklenebilir, başarılı ve etkin yuvadır. A alanı içeriği güncellendiği ancak henüz tamamlanmadığı için A alanı başlatılamaz olarak işaretlendi. Bu durumda yeniden başlatma işlemi, B yuvasından başlatılmaya devam etmelidir.
  • Güncelleme uygulandı, yeniden başlatma bekleniyor: Sistem B yuvasından çalışıyor, B yuvası başlatılabilir ve başarılı ancak A yuvası etkin olarak işaretlendi (bu nedenle başlatılabilir olarak işaretlendi). A yuvası henüz başarılı olarak işaretlenmedi ve önyükleyici tarafından A yuvasından başlatma için belirli sayıda deneme yapılması gerekiyor.
  • Sistem yeni güncellemede yeniden başlatıldı: Sistem ilk kez A yuvasından çalışıyor, B yuvası hâlâ başlatılabilir ve başarılıyken A yuvası yalnızca başlatılabilir ve hâlâ etkin ancak başarılı değil. Bir kullanıcı alanı arka plan programı, update_verifier, bazı kontroller yapıldıktan sonra A yuvasını başarılı olarak işaretlemelidir.

Akış güncelleme desteği

Kullanıcı cihazlarında güncelleme paketini indirmek için her zaman yeterli alan olmayabilir./data Ne OEM'ler ne de kullanıcılar /cache bölümünde yer kaybetmek istemediğinden bazı kullanıcılar, cihazda güncelleme paketini depolayacak yer olmadığı için güncellemeleri alamaz. Bu sorunu gidermek için Android 8.0, blokları /data üzerinde depolamaya gerek kalmadan indirilirken doğrudan B bölümüne yazan A/B güncellemelerinin yayınlanması için destek ekledi. Yayın A/B güncelleme işlemleri 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ılıp kullanılmadığına bakılmaksızın Android 7.1 ve sonraki sürümlerde yayın A/B güncellemelerini desteklemek için gereklidir.

A/B güncellemesinin ömrü

Güncelleme işlemi, OTA paketi (kodda yük olarak adlandırılır) indirilebilir olduğunda başlar. Cihazdaki politikalar; pil seviyesi, kullanıcı etkinliği, şarj durumu veya diğer politikalara göre yükün indirilmesini ve uygulanmasını erteleyebilir. Ayrıca, güncelleme arka planda çalıştığı için kullanıcılar güncelleme işleminin devam ettiğini fark etmeyebilir. Tüm bunlar, politika, beklenmedik yeniden başlatma veya kullanıcı işlemleri nedeniyle güncelleme sürecinin herhangi bir noktada kesintiye uğrayabileceği anlamına gelir.

İsteğe bağlı olarak, OTA paketindeki meta veriler güncellemenin yayınlanabileceğini gösterir. Aynı paket, yayınlanmayan yüklemeler için de kullanılabilir. Sunucu, istemciye yayın yaptığını bildirmek için meta verileri kullanabilir. Böylece istemci, OTA'yı update_engine doğru şekilde aktarır. Kendi sunucusu ve istemcisi olan cihaz üreticileri, sunucunun güncellemenin yayınlandığını tanımlamasını (veya tüm güncellemelerin yayınlandığını varsaymasını) ve istemcinin yayın için update_engine'ya doğru çağrıyı yapmasını sağlayarak yayın güncellemelerini etkinleştirebilir. Üreticiler, paketin akış varyantı olduğunu kullanarak istemciye bir işaret gönderebilir ve akış olarak çerçeve tarafına aktarımı tetikleyebilir.

Bir yük kullanılabilir hale geldikten sonra güncelleme süreci şu şekilde ilerler:

Adım Etkinlikler
1 Mevcut yuva (veya "kaynak yuva"), markBootSuccessful() ile başarılı olarak işaretlenir (daha önce işaretlenmemişse).
2 Kullanılmayan yuva (veya "hedef yuva"), setSlotAsUnbootable() işlevi çağrılarak başlatılamaz olarak işaretlenir. Önyükleyicinin, kısa süre içinde geçersiz veriler içerecek olan kullanılmayan yuvaya geri dönmesini ö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 bozulmuş olsa bile (ör. kilitlenme döngüsündeki kullanıcı arayüzü) mevcut alan başarılı olarak işaretlenir. Çünkü bu sorunları düzeltmek için yeni yazılım gönderilebilir.

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 yükünün nispeten küçük bir kısmı olan meta veriler, hedef yuvada yeni sürümü oluşturup doğrulamak için gereken işlemlerin listesini içerir. Örneğin, bir işlem belirli bir blob'u sıkıştırmayı açıp hedef bölümlendirmedeki belirli bloklara yazabilir veya kaynak bölümlendirmeden okuyup ikili yama uygulayarak hedef bölümlendirmedeki belirli bloklara yazabilir.
  • Ekstra veriler. Güncelleme yükünün büyük bir kısmını oluşturan, işlemlerle ilişkili ek veriler bu örneklerde sıkıştırılmış blob veya ikili yama olarak gösterilir.
3 Yük meta verileri indirilir.
4 Meta verilerde tanımlanan her işlem için ilişkili veriler (varsa) sırayla 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 ile karşılaştırılarak 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 oluşursa güncelleme başarısız olur ve muhtemelen farklı bir yükle yeniden denenir. Şu ana kadar tüm adımlar başarılı olduysa güncelleme başarılı olur ve son adım yürütülür.
7 setActiveBootSlot() çağrılarak kullanılmayan yuva etkin olarak işaretlenir. Kullanılmayan yuvanın etkin olarak işaretlenmesi, başlatma işleminin tamamlanacağı anlamına gelmez. Başlatıcı (veya sistemin kendisi), başarılı bir durum okumazsa etkin yuva değiştirilebilir.
8 Kurulum sonrası (aşağıda açıklanmıştır), eski sürümde çalışmaya devam ederken "yeni güncelleme" sürümünden bir program çalıştırmayı içerir. OTA paketinde tanımlanmışsa bu adım zorunludur ve program 0 çıkış koduyla geri dönmelidir. Aksi takdirde güncelleme başarısız olur.
9 Sistem yeni yuvada yeterince başarılı bir şekilde başlatılıp yeniden başlatma sonrası kontroller tamamlandıktan sonra, artık geçerli olan 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 monte eder ve OTA'da belirtilen programı monte edilmiş 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ımlanmışsa kullanılmayan yuvadaki bu bölüm sabit bir konuma (ör. /postinstall_mount) yerleştirilir ve /postinstall_mount/usr/bin/postinstall komutu yürütülür.

Yükleme sonrası işlemin başarılı olması için eski çekirdek şunları yapabilmelidir:

  • Yeni dosya sistemi biçimini bağlayın. Dosya sistemi türü, eski çekirdekte desteklenmediği sürece değiştirilemez.Sıkıştırılmış dosya sistemi (ör. SquashFS) kullanılıyorsa kullanılan sıkıştırma algoritması gibi ayrıntılar da bu kapsamdadır.
  • Yeni bölümün yükleme sonrası program biçimini anlama. Yürütülebilir ve Bağlanabilir Biçim (ELF) ikilisi kullanılıyorsa eski çekirdekle uyumlu olmalıdır (ör. mimari 32 bitlik derlemelerden 64 bitlik derlemelere geçiş yaptıysa eski 32 bitlik çekirdekte çalışan 64 bitlik yeni bir program). Yükleyiciye (ld) başka yollar kullanması veya statik bir ikili dosya oluşturması talimatı verilmediğ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 ikilisi tarafından yorumlanan bir kabuk komut dosyasını (en üstte #! işaretçisiyle) yükleme sonrası program olarak kullanabilir, ardından daha karmaşık bir ikili yükleme sonrası programı yürütmek için yeni ortamdan kitaplık yolları ayarlayabilirsiniz. Alternatif olarak, ana sistem bölümündeki dosya sistemi biçiminin geriye dönük uyumluluk sorunlarına veya geçiş güncelleme işlemlerine neden olmadan güncellenmesini sağlamak için yükleme sonrası adımı özel bir küçük bölümden de çalıştırabilirsiniz. Bu sayede kullanıcılar, fabrika görüntüsünden doğrudan en son sürüme güncelleyebilir.

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 girişim görevlerini gerçekleştirmek için uygundur. Yükleme sonrası adım, yeniden başlatmadan önce tek seferlik hata düzeltmeleri için uygun değildir. Bu düzeltmeler, öngörülemeyen izinler gerektirir.

Seçilen yükleme sonrası program, postinstall SELinux bağlamında çalışır. Yeni bağlanan bölümdeki tüm dosyalar, yeni sisteme yeniden başlatıldıktan sonraki ö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 işleminden sonra

Yeniden başlatma işleminden sonra update_verifier, dm-verity kullanarak bütünlük denetimini tetikler. Bu kontrol, güvenli geri alma işlemini engelleyecek geri döndürülemez değişiklikler yapılmasını önlemek için 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 başlatma başarılı olarak işaretlenir.

update_verifier, AOSP kodu kullanılırken A/B OTA paketine dahil edilen /data/ota_package/care_map.txt içinde 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ümde başarıyla başlatıldıktan sonra çıkarılan dosyayı siler.