Bản cập nhật hệ thống A/B, còn được gọi là bản cập nhật liền mạch, đảm bảo hệ thống khởi động khả thi vẫn còn trên đĩa trong quá trình cập nhật qua mạng (OTA) . Cách tiếp cận này giúp giảm khả năng thiết bị không hoạt động sau khi cập nhật, điều đó có nghĩa là ít phải thay thế thiết bị hơn và thiết bị bị nhấp nháy tại các trung tâm sửa chữa và bảo hành. Các hệ điều hành cấp thương mại khác như ChromeOS cũng sử dụng thành công các bản cập nhật A/B.
Để biết thêm thông tin về các bản cập nhật hệ thống A/B và cách chúng hoạt động, hãy xem Lựa chọn phân vùng (khe cắm) .
Cập nhật hệ thống A/B mang lại những lợi ích sau:
- Cập nhật OTA có thể xảy ra trong khi hệ thống đang chạy mà không làm gián đoạn người dùng. Người dùng có thể tiếp tục sử dụng thiết bị của họ trong quá trình OTA—thời gian ngừng hoạt động duy nhất trong quá trình cập nhật là khi thiết bị khởi động lại vào phân vùng ổ đĩa đã cập nhật.
- Sau khi cập nhật, việc khởi động lại không mất nhiều thời gian như khởi động lại thông thường.
- Nếu OTA không áp dụng được (ví dụ: do lỗi flash), người dùng sẽ không bị ảnh hưởng. Người dùng sẽ tiếp tục chạy hệ điều hành cũ và khách hàng có thể tự do thử cập nhật lại.
- Nếu một bản cập nhật OTA được áp dụng nhưng không khởi động được, thiết bị sẽ khởi động lại vào phân vùng cũ và vẫn có thể sử dụng được. Khách hàng có thể thử lại bản cập nhật.
- Bất kỳ lỗi nào (chẳng hạn như lỗi I/O) chỉ ảnh hưởng đến tập hợp phân vùng chưa sử dụng và có thể thử lại. Những lỗi như vậy cũng ít xảy ra hơn vì tải I/O được cố ý ở mức thấp để tránh làm giảm trải nghiệm người dùng.
- Các bản cập nhật có thể được truyền trực tuyến tới các thiết bị A/B, loại bỏ nhu cầu tải xuống gói trước khi cài đặt. Phát trực tuyến có nghĩa là người dùng không cần phải có đủ dung lượng trống để lưu trữ gói cập nhật trên
/data
hoặc/cache
. - Phân vùng bộ đệm không còn được sử dụng để lưu trữ các gói cập nhật OTA, vì vậy không cần đảm bảo rằng phân vùng bộ đệm đủ lớn cho các bản cập nhật trong tương lai.
- dm-verity đảm bảo thiết bị sẽ khởi động một hình ảnh nguyên vẹn. Nếu thiết bị không khởi động do sự cố OTA hoặc dm-verity không tốt, thiết bị có thể khởi động lại vào một hình ảnh cũ. ( Khởi động được xác minh của Android không yêu cầu cập nhật A/B.)
Giới thiệu về cập nhật hệ thống A/B
Cập nhật A/B yêu cầu thay đổi đối với cả máy khách và hệ thống. Tuy nhiên, máy chủ gói OTA không yêu cầu thay đổi: các gói cập nhật vẫn được cung cấp qua HTTPS. Đối với các thiết bị sử dụng cơ sở hạ tầng OTA của Google, tất cả các thay đổi hệ thống đều nằm trong AOSP và mã máy khách được cung cấp bởi các dịch vụ của Google Play. Các OEM không sử dụng cơ sở hạ tầng OTA của Google sẽ có thể sử dụng lại mã hệ thống AOSP nhưng sẽ cần cung cấp ứng dụng khách của riêng họ.
Đối với các OEM cung cấp cho khách hàng của chính họ, khách hàng cần:
- Quyết định thời điểm thực hiện cập nhật. Bởi vì các cập nhật A/B diễn ra trong nền nên chúng không còn do người dùng thực hiện nữa. Để tránh làm phiền người dùng, bạn nên lên lịch cập nhật khi thiết bị ở chế độ bảo trì không hoạt động, chẳng hạn như qua đêm và trên Wi-Fi. Tuy nhiên, khách hàng của bạn có thể sử dụng bất kỳ phỏng đoán nào bạn muốn.
- Kiểm tra với các máy chủ gói OTA của bạn và xác định xem có bản cập nhật nào không. Mã này hầu như giống với mã máy khách hiện tại của bạn, ngoại trừ việc bạn muốn báo hiệu rằng thiết bị hỗ trợ A/B. (Ứng dụng khách của Google cũng bao gồm nút Kiểm tra ngay để người dùng kiểm tra bản cập nhật mới nhất.)
- Gọi
update_engine
bằng URL HTTPS cho gói cập nhật của bạn, giả sử có sẵn một gói.update_engine
sẽ cập nhật các khối thô trên phân vùng hiện không được sử dụng khi truyền gói cập nhật. - Báo cáo cài đặt thành công hoặc lỗi cho máy chủ của bạn, dựa trên mã kết quả
update_engine
. Nếu bản cập nhật được áp dụng thành công,update_engine
sẽ yêu cầu bộ tải khởi động khởi động vào hệ điều hành mới trong lần khởi động lại tiếp theo. Bộ tải khởi động sẽ dự phòng cho HĐH cũ nếu HĐH mới không khởi động được, vì vậy máy khách không cần thực hiện công việc nào. Nếu cập nhật không thành công, khách hàng cần quyết định khi nào (và có nên) thử lại hay không, dựa trên mã lỗi chi tiết. Ví dụ: một khách hàng tốt có thể nhận ra rằng gói OTA một phần ("khác") không thành công và thay vào đó hãy thử gói OTA đầy đủ.
Theo tùy chọn, khách hàng có thể:
- Hiển thị thông báo yêu cầu người dùng khởi động lại. Nếu bạn muốn triển khai một chính sách mà người dùng được khuyến khích cập nhật thường xuyên thì có thể thêm thông báo này vào ứng dụng khách của bạn. Nếu ứng dụng khách không nhắc người dùng thì người dùng vẫn sẽ nhận được bản cập nhật vào lần khởi động lại tiếp theo. (Ứng dụng khách của Google có độ trễ có thể định cấu hình cho mỗi lần cập nhật.)
- Hiển thị thông báo cho người dùng biết liệu họ đã khởi động vào phiên bản hệ điều hành mới hay họ dự kiến sẽ làm như vậy nhưng lại quay trở lại phiên bản hệ điều hành cũ. (Khách hàng của Google thường không làm như vậy.)
Về phía hệ thống, các bản cập nhật hệ thống A/B ảnh hưởng đến những điều sau:
- Lựa chọn phân vùng (khe cắm), daemon
update_engine
và tương tác bộ nạp khởi động (được mô tả bên dưới) - Quy trình xây dựng và tạo gói cập nhật OTA (được mô tả trong Triển khai cập nhật A/B )
Lựa chọn phân vùng (khe)
Bản cập nhật hệ thống A/B sử dụng hai bộ phân vùng được gọi là vị trí (thường là vị trí A và vị trí B). Hệ thống chạy từ khe hiện tại trong khi các phân vùng trong khe không sử dụng không được truy cập bởi hệ thống đang chạy trong quá trình hoạt động bình thường. Cách tiếp cận này làm cho các bản cập nhật có khả năng chống lỗi bằng cách giữ vị trí không sử dụng làm dự phòng: Nếu xảy ra lỗi trong hoặc ngay sau khi cập nhật, hệ thống có thể quay trở lại vị trí cũ và tiếp tục có một hệ thống hoạt động. Để đạt được mục tiêu này, không có phân vùng nào được sử dụng bởi vị trí hiện tại phải được cập nhật như một phần của bản cập nhật OTA (bao gồm các phân vùng chỉ có một bản sao).
Mỗi vị trí có một thuộc tính có thể khởi động cho biết liệu vị trí đó có chứa đúng hệ thống mà thiết bị có thể khởi động hay không. Khe hiện tại có khả năng khởi động khi hệ thống đang chạy, nhưng khe còn lại có thể có phiên bản cũ (vẫn đúng) của hệ thống, phiên bản mới hơn hoặc dữ liệu không hợp lệ. Bất kể vị trí hiện tại là gì, có một vị trí là vị trí hoạt động (khe mà bộ tải khởi động sẽ khởi động từ lần khởi động tiếp theo) hoặc vị trí ưu tiên .
Mỗi vị trí cũng có một thuộc tính thành công do không gian người dùng đặt, thuộc tính này chỉ liên quan nếu vị trí đó cũng có khả năng khởi động. Một vị trí thành công sẽ có thể tự khởi động, chạy và cập nhật. Một khe cắm có thể khởi động không được đánh dấu là thành công (sau nhiều lần thử khởi động từ nó) sẽ được bộ nạp khởi động đánh dấu là không thể khởi động, bao gồm cả việc thay đổi khe cắm đang hoạt động sang một khe cắm có thể khởi động khác (thường là khe cắm đang chạy ngay trước khi cố gắng khởi động vào cái mới, đang hoạt động). Các chi tiết cụ thể của giao diện được xác định trong boot_control.h
.
Cập nhật daemon động cơ
Các bản cập nhật hệ thống A/B sử dụng một trình nền nền có tên update_engine
để chuẩn bị cho hệ thống khởi động vào một phiên bản cập nhật mới. Daemon này có thể thực hiện các hành động sau:
- Đọc từ các phân vùng A/B của khe hiện tại và ghi bất kỳ dữ liệu nào vào các phân vùng A/B của khe chưa sử dụng theo hướng dẫn của gói OTA.
- Gọi giao diện
boot_control
trong quy trình làm việc được xác định trước. - Chạy chương trình hậu cài đặt từ phân vùng mới sau khi ghi tất cả các phân vùng vị trí chưa sử dụng, theo hướng dẫn của gói OTA. (Để biết chi tiết, xem Sau khi cài đặt ).
Vì trình update_engine
không tham gia vào quá trình khởi động, nó bị hạn chế về những gì nó có thể thực hiện trong quá trình cập nhật bởi các chính sách và tính năng của SELinux trong vị trí hiện tại (không thể cập nhật các chính sách và tính năng đó cho đến khi hệ thống khởi động vào phiên bản mới). Để duy trì một hệ thống mạnh mẽ, quá trình cập nhật không được sửa đổi bảng phân vùng, nội dung của các phân vùng trong vị trí hiện tại hoặc nội dung của các phân vùng không phải A/B không thể xóa bằng khôi phục cài đặt gốc.
Cập nhật nguồn động cơ
Nguồn update_engine
nằm ở system/update_engine
. Các tệp dexopt A/B OTA được phân chia giữa installd
và trình quản lý gói:
-
frameworks/native/cmds/installd/
ota* bao gồm tập lệnh postinstall, tệp nhị phân cho chroot, bản sao installd gọi dex2oat, tập lệnh tạo tác di chuyển hậu OTA và tệp rc cho tập lệnh di chuyển. -
frameworks/base/services/core/java/com/android/server/pm/OtaDexoptService.java
(cộng vớiOtaDexoptShellCommand
) là trình quản lý gói chuẩn bị các lệnh dex2oat cho các ứng dụng.
Để biết ví dụ hoạt động, hãy tham khảo /device/google/marlin/device-common.mk
.
Cập nhật nhật ký động cơ
Đối với các bản phát hành Android 8.x trở về trước, bạn có thể tìm thấy nhật ký update_engine
trong logcat
và trong báo cáo lỗi. Để cung cấp nhật ký update_engine
trong hệ thống tệp, hãy vá các thay đổi sau vào bản dựng của bạn:
Những thay đổi này lưu một bản sao của nhật ký update_engine
gần đây nhất vào /data/misc/update_engine_log/update_engine. YEAR - TIME
. Ngoài nhật ký hiện tại, năm nhật ký gần đây nhất được lưu trong /data/misc/update_engine_log/
. Người dùng có ID nhóm nhật ký sẽ có thể truy cập nhật ký hệ thống tệp.
Tương tác bộ nạp khởi động
HAL boot_control
được sử dụng bởi update_engine
(và có thể cả các daemon khác) để hướng dẫn bộ tải khởi động khởi động từ đâu. Các tình huống ví dụ phổ biến và các trạng thái liên quan của chúng bao gồm:
- Trường hợp bình thường : Hệ thống đang chạy từ vị trí hiện tại của nó, vị trí A hoặc vị trí B. Không có bản cập nhật nào được áp dụng cho đến nay. Vị trí hiện tại của hệ thống có thể khởi động, thành công và vị trí đang hoạt động.
- Đang cập nhật : Hệ thống đang chạy từ khe B, vì vậy khe B là khe có thể khởi động, thành công và đang hoạt động. Khe A được đánh dấu là không thể khởi động do nội dung của khe A đang được cập nhật nhưng chưa hoàn thành. Khởi động lại ở trạng thái này sẽ tiếp tục khởi động từ khe B.
- Đã áp dụng bản cập nhật, đang chờ khởi động lại : Hệ thống đang chạy từ khe B, khe B có thể khởi động và thành công, nhưng khe A được đánh dấu là đang hoạt động (và do đó được đánh dấu là có thể khởi động). Khe A chưa được đánh dấu là thành công và một số lần thử khởi động từ khe A phải được thực hiện bởi bộ tải khởi động.
- Hệ thống được khởi động lại vào bản cập nhật mới : Hệ thống đang chạy từ khe A lần đầu tiên, khe B vẫn có khả năng khởi động và thành công trong khi khe A chỉ có khả năng khởi động và vẫn hoạt động nhưng không thành công. Trình nền không gian người dùng,
update_verifier
, sẽ đánh dấu vị trí A là thành công sau khi thực hiện một số kiểm tra.
Hỗ trợ cập nhật trực tuyến
Thiết bị của người dùng không phải lúc nào cũng có đủ dung lượng trên /data
để tải xuống gói cập nhật. Vì cả OEM và người dùng đều không muốn lãng phí dung lượng trên phân vùng /cache
, một số người dùng không có bản cập nhật vì thiết bị không có nơi nào để lưu trữ gói cập nhật. Để giải quyết vấn đề này, Android 8.0 đã thêm hỗ trợ truyền trực tuyến các bản cập nhật A/B ghi các khối trực tiếp vào phân vùng B khi chúng được tải xuống mà không cần phải lưu trữ các khối trên /data
. Truyền trực tuyến các bản cập nhật A/B hầu như không cần bộ nhớ tạm thời và chỉ yêu cầu đủ bộ nhớ cho khoảng 100 KiB siêu dữ liệu.
Để bật cập nhật phát trực tuyến trong Android 7.1, hãy chọn các bản vá sau:
- Cho phép hủy yêu cầu giải quyết proxy
- Khắc phục việc chấm dứt chuyển khoản trong khi giải quyết proxy
- Thêm bài kiểm tra đơn vị cho TerminateTransfer giữa các phạm vi
- Dọn dẹp RetryTimeoutCallback()
Cần có các bản vá này để hỗ trợ phát trực tuyến các bản cập nhật A/B trong Android 7.1 trở lên cho dù sử dụng Google Mobile Services (GMS) hay bất kỳ ứng dụng khách cập nhật nào khác.
Cuộc sống của bản cập nhật A/B
Quá trình cập nhật bắt đầu khi gói OTA (được gọi bằng mã là payload ) có sẵn để tải xuống. Các chính sách trong thiết bị có thể trì hoãn tải xuống tải trọng và ứng dụng dựa trên mức pin, hoạt động của người dùng, trạng thái sạc hoặc các chính sách khác. Ngoài ra, vì bản cập nhật chạy ẩn nên người dùng có thể không biết bản cập nhật đang diễn ra. Tất cả điều này có nghĩa là quá trình cập nhật có thể bị gián đoạn bất kỳ lúc nào do chính sách, khởi động lại không mong muốn hoặc hành động của người dùng.
Theo tùy chọn, bản thân siêu dữ liệu trong gói OTA cho biết bản cập nhật có thể được phát trực tuyến; gói tương tự cũng có thể được sử dụng để cài đặt không phát trực tuyến. Máy chủ có thể sử dụng siêu dữ liệu để thông báo cho khách hàng rằng nó đang phát trực tuyến để khách hàng chuyển giao OTA cho update_engine
một cách chính xác. Các nhà sản xuất thiết bị có máy chủ và ứng dụng khách của riêng họ có thể bật cập nhật trực tuyến bằng cách đảm bảo máy chủ xác định bản cập nhật đang phát trực tuyến (hoặc giả sử tất cả các bản cập nhật đều đang phát trực tuyến) và khách hàng thực hiện cuộc gọi chính xác tới update_engine
để phát trực tuyến. Các nhà sản xuất có thể sử dụng thực tế là gói thuộc biến thể phát trực tuyến để gửi cờ đến máy khách nhằm kích hoạt chuyển giao sang phía khung dưới dạng phát trực tuyến.
Sau khi có tải trọng, quy trình cập nhật như sau:
Bước chân | Các hoạt động |
---|---|
1 | Vị trí hiện tại (hoặc "vị trí nguồn") được đánh dấu là thành công (nếu chưa được đánh dấu) bằng markBootSuccessful() . |
2 | Vị trí không sử dụng (hoặc "vị trí mục tiêu") được đánh dấu là không thể khởi động bằng cách gọi hàm setSlotAsUnbootable() . Vị trí hiện tại luôn được đánh dấu là thành công khi bắt đầu cập nhật để ngăn bộ tải khởi động quay trở lại vị trí không sử dụng, sẽ sớm có dữ liệu không hợp lệ. Nếu hệ thống đã đạt đến điểm có thể bắt đầu áp dụng bản cập nhật, thì vị trí hiện tại được đánh dấu là thành công ngay cả khi các thành phần chính khác bị hỏng (chẳng hạn như giao diện người dùng trong vòng lặp sự cố) vì có thể đẩy phần mềm mới để sửa những lỗi này các vấn đề.Tải trọng cập nhật là một đốm mờ đục với hướng dẫn cập nhật lên phiên bản mới. Tải trọng cập nhật bao gồm:
|
3 | Siêu dữ liệu tải trọng được tải xuống. |
4 | Đối với mỗi thao tác được xác định trong siêu dữ liệu, theo thứ tự, dữ liệu liên quan (nếu có) được tải xuống bộ nhớ, thao tác được áp dụng và bộ nhớ liên quan bị loại bỏ. |
5 | Toàn bộ phân vùng được đọc lại và xác minh dựa trên hàm băm dự kiến. |
6 | Bước hậu cài đặt (nếu có) được chạy. Trong trường hợp xảy ra lỗi trong quá trình thực hiện bất kỳ bước nào, bản cập nhật không thành công và được thử lại với một trọng tải khác. Nếu tất cả các bước cho đến nay đã thành công, cập nhật thành công và bước cuối cùng được thực hiện. |
7 | Vị trí không sử dụng được đánh dấu là đang hoạt động bằng cách gọi setActiveBootSlot() . Đánh dấu khe chưa sử dụng là đang hoạt động không có nghĩa là nó sẽ khởi động xong. Bộ tải khởi động (hoặc chính hệ thống) có thể chuyển vị trí hoạt động trở lại nếu nó không đọc được trạng thái thành công. |
số 8 | Quá trình cài đặt sau (được mô tả bên dưới) liên quan đến việc chạy một chương trình từ phiên bản "cập nhật mới" trong khi vẫn chạy ở phiên bản cũ. Nếu được xác định trong gói OTA, bước này là bắt buộc và chương trình phải trả về mã thoát 0 ; nếu không, cập nhật không thành công. | 9 | Sau khi hệ thống khởi động thành công đủ xa vào vị trí mới và hoàn tất quá trình kiểm tra sau khi khởi động lại, vị trí hiện tại (trước đây là "vị trí mục tiêu") được đánh dấu là thành công bằng cách gọi markBootSuccessful() . |
Bài cài đặt
Đối với mọi phân vùng có bước cài đặt sau được xác định, update_engine
sẽ gắn phân vùng mới vào một vị trí cụ thể và thực thi chương trình được chỉ định trong OTA tương ứng với phân vùng đã gắn. Ví dụ: nếu chương trình sau khi cài đặt được xác định là usr/bin/postinstall
trong phân vùng hệ thống, phân vùng này từ khe không sử dụng sẽ được gắn vào một vị trí cố định (chẳng hạn như /postinstall_mount
) và /postinstall_mount/usr/bin/postinstall
lệnh /postinstall_mount/usr/bin/postinstall
được thực thi.
Để quá trình cài đặt sau thành công, hạt nhân cũ phải có khả năng:
- Gắn định dạng hệ thống tập tin mới . Loại hệ thống tệp không thể thay đổi trừ khi có hỗ trợ cho nó trong nhân cũ, bao gồm các chi tiết như thuật toán nén được sử dụng nếu sử dụng hệ thống tệp nén (tức là SquashFS).
- Hiểu định dạng chương trình sau khi cài đặt của phân vùng mới . Nếu sử dụng nhị phân Định dạng có thể thực thi và có thể liên kết (ELF), thì nó phải tương thích với nhân cũ (ví dụ: chương trình mới 64 bit chạy trên nhân 32 bit cũ nếu kiến trúc chuyển từ bản dựng 32 sang 64 bit). Trừ khi trình tải (
ld
) được hướng dẫn sử dụng các đường dẫn khác hoặc xây dựng tệp nhị phân tĩnh, các thư viện sẽ được tải từ hình ảnh hệ thống cũ chứ không phải hình ảnh mới.
Ví dụ: bạn có thể sử dụng tập lệnh shell làm chương trình sau khi cài đặt được diễn giải bởi hệ nhị phân shell của hệ thống cũ bằng dấu #!
điểm đánh dấu ở trên cùng), sau đó thiết lập đường dẫn thư viện từ môi trường mới để thực thi chương trình hậu cài đặt nhị phân phức tạp hơn. Ngoài ra, bạn có thể chạy bước hậu cài đặt từ một phân vùng chuyên dụng nhỏ hơn để cho phép cập nhật định dạng hệ thống tệp trong phân vùng hệ thống chính mà không phát sinh các sự cố tương thích ngược hoặc các bản cập nhật nền tảng; điều này sẽ cho phép người dùng cập nhật trực tiếp lên phiên bản mới nhất từ hình ảnh xuất xưởng.
Chương trình sau khi cài đặt mới bị giới hạn bởi các chính sách SELinux được xác định trong hệ thống cũ. Do đó, bước sau khi cài đặt phù hợp để thực hiện các tác vụ theo yêu cầu của thiết kế trên một thiết bị nhất định hoặc các tác vụ nỗ lực cao nhất khác (tức là cập nhật chương trình cơ sở hoặc bộ nạp khởi động có khả năng A/B, chuẩn bị các bản sao cơ sở dữ liệu cho phiên bản mới, v.v. ). Bước sau khi cài đặt không phù hợp với các bản sửa lỗi một lần trước khi khởi động lại yêu cầu các quyền không lường trước được.
Chương trình sau khi cài đặt đã chọn chạy trong ngữ cảnh SELinux postinstall
. Tất cả các tệp trong phân vùng được gắn mới sẽ được gắn thẻ postinstall_file
, bất kể thuộc tính của chúng là gì sau khi khởi động lại vào hệ thống mới đó. Các thay đổi đối với thuộc tính SELinux trong hệ thống mới sẽ không ảnh hưởng đến bước cài đặt sau. Nếu chương trình sau khi cài đặt cần thêm quyền, thì những quyền đó phải được thêm vào ngữ cảnh sau khi cài đặt.
Sau khi khởi động lại
Sau khi khởi động lại, update_verifier
kích hoạt kiểm tra tính toàn vẹn bằng dm-verity. Quá trình kiểm tra này bắt đầu trước hợp tử để tránh các dịch vụ Java thực hiện bất kỳ thay đổi không thể đảo ngược nào có thể ngăn quá trình khôi phục an toàn. Trong quá trình này, bộ tải khởi động và nhân cũng có thể kích hoạt khởi động lại nếu khởi động đã được xác minh hoặc dm-verity phát hiện bất kỳ lỗi nào. Sau khi kiểm tra hoàn tất, update_verifier
đánh dấu khởi động thành công.
update_verifier
sẽ chỉ đọc các khối được liệt kê trong /data/ota_package/care_map.txt
, được bao gồm trong gói A/B OTA khi sử dụng mã AOSP. Ứng dụng khách cập nhật hệ thống Java, chẳng hạn như GmsCore, trích xuất care_map.txt
, thiết lập quyền truy cập trước khi khởi động lại thiết bị và xóa tệp đã giải nén sau khi hệ thống khởi động thành công vào phiên bản mới.