Cập nhật hệ thống A/B (liền mạch)

Cập nhật hệ thống A/B, còn được gọi là 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, đồng nghĩa với việc ít phải thay thế thiết bị hơn và ít xảy ra hiện tượng phản hồi thiết bị hơn 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 (vị trí) .

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 mình trong thời gian 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, quá trình khởi động lại không mất nhiều thời gian hơn quá trình khởi động lại thông thường.
  • Nếu OTA không áp dụng được (ví dụ do đèn flash kém), 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ể thử cập nhật lại.
  • Nếu áp dụng bản cập nhật OTA 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 bộ phân vùng chưa sử dụng và có thể được thử lại. Những lỗi như vậy cũng ít xảy ra hơn do tải I/O được cố tình giảm 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. Truyền phát có nghĩa là người dùng không cần 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 nên 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 không bị hỏng. Nếu thiết bị không khởi động được do sự cố OTA hoặc dm-verity không tốt, thiết bị có thể khởi động lại vào 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ả khách hàng 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, các thay đổi của hệ thống đều nằm trong AOSP và mã máy khách được cung cấp bởi dịch vụ 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 cho ứ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 khi nào cần cập nhật. Vì cập nhật A/B diễn ra ở chế độ 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, nên lập 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 pháp phỏng đoán nào bạn muốn.
  • Kiểm tra với máy chủ gói OTA của bạn và xác định xem có bản cập nhật hay không. Mã này gần 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. 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 nó truyền gói cập nhật.
  • Báo cáo cài đặt thành công hoặc thất bạ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ẽ báo cho bộ nạp 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ẽ quay trở lại hệ điều hành cũ nếu hệ điều hành mới không khởi động được, do đó máy khách không cần thực hiện bất kỳ thao tá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ó) 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 biệt") không thành công và thay vào đó hãy thử gói OTA đầy đủ.

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 chính sách trong đó người dùng được khuyến khích cập nhật thường xuyên thì thông báo này có thể được thêm vào ứng dụng khách của bạn. Nếu máy 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 tiếp theo khi họ khởi động lại. (Ứ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 họ đã khởi động vào phiên bản hệ điều hành mới hay họ dự kiến ​​làm như vậy nhưng lại quay 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 cắm)

Các 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 hệ thống đang chạy truy cập trong quá trình hoạt động bình thường. Cách tiếp cận này giúp các bản cập nhật có khả năng chống lỗi bằng cách giữ lại vị trí không được 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ó hệ thống hoạt động. Để đạt được mục tiêu này, không được cập nhật phân vùng nào được sử dụng bởi vị trí hiện tại như một phần của bản cập nhật OTA (bao gồm cả các phân vùng chỉ có một bản sao).

Mỗi khe cắm có một thuộc tính có khả năng khởi động cho biết khe cắm đó có chứa hệ thống chính xác 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 kia 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ể khe cắm hiện tại là gì, vẫn có một khe cắm là khe hoạt động (khe mà bộ nạp khởi động sẽ khởi động từ lần khởi động tiếp theo) hoặc khe ưa thích .

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 khe cắm thành công phải có khả năng tự khởi động, chạy và cập nhật. Một khe có khả năng khởi động không được đánh dấu là thành công (sau một số lần thử khởi động từ nó) phải được bộ tải khởi động đánh dấu là không thể khởi động, bao gồm cả việc thay đổi khe hoạt động sang một khe có khả năng khởi động khác (thông thường là khe chạy ngay trước khi thử khởi động). sang cái mới, tích cực). 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 daemon nền có tên update_engine để chuẩn bị cho hệ thống khởi động vào 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 theo quy trình làm việc được xác định trước.
  • Chạy chương trình sau cài đặt từ phân vùng mới sau khi ghi tất cả các phân vùng khe 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ì daemon update_engine không tham gia vào quá trình khởi động nên 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 SELinux trong khe hiện tại (các chính sách và tính năng đó không thể được cập nhật cho đến khi hệ thống khởi động vào một 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 khe 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 được 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 trong 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:

Để 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

boot_control HAL được update_engine (và có thể cả các daemon khác) sử dụng để hướng dẫn bộ nạp khởi động những gì cần khởi động. 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 B. Cho đến nay chưa có bản cập nhật nào được áp dụng. Khe hiện tại của hệ thống có khả năng khởi động, thành công và khe đang hoạt động.
  • Đang cập nhật : Hệ thống đang chạy từ slot B nên slot B là slot có khả năng 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 được 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ó khả năng 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ó khả năng khởi động). Khe A vẫn chưa được đánh dấu là thành công và bộ nạp khởi động phải thực hiện một số lần thử khởi động từ khe A.
  • 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 khởi động được và thành công trong khi khe A chỉ khởi động được 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 phá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 nên 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ợ phát 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ỉ cần bộ nhớ vừa đủ 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:

Các bản vá này được yêu cầu để 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 Dịch vụ di động của Google (GMS) hay bất kỳ ứng dụng 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 trong mã là tải trọng ) có sẵn để tải xuống. Các chính sách trong thiết bị có thể trì hoãn việc 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, do bản cập nhật chạy ở chế độ nề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 đột ngột hoặc hành động của người dùng.

Theo tùy chọ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 máy khách biết nó đang phát trực tuyến để máy khách sẽ chuyển 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à máy khách riêng của họ có thể bật tính năng phát trực tuyến các bản cập nhật 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 đang phát trực tuyến) và khách hàng thực hiện lệnh 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ể lợi dụng thực tế là gói này 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 hệ thống dưới dạng phát trực tuyến.

Sau khi có tải trọng, quá 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 "khe 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 được sử dụng (hoặc "khe đích") được đánh dấu là không thể khởi động bằng cách gọi hàm setSlotAsUnbootable() . Khe 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 bootloader rơi trở lại khe chưa 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ì khe 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 gặp sự cố trong vòng lặp) 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àu 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:
  • Metadata . Là một phần tương đối nhỏ của tải trọng cập nhật, siêu dữ liệu chứa danh sách các thao tác để tạo và xác minh phiên bản mới trên vùng mục tiêu. Ví dụ: một thao tác có thể giải nén một blob nhất định và ghi nó vào các khối cụ thể trong phân vùng đích hoặc đọc từ phân vùng nguồn, áp dụng bản vá nhị phân và ghi vào các khối nhất định trong phân vùng đích.
  • Dữ liệu bổ sung . Là phần lớn trọng tải cập nhật, dữ liệu bổ sung liên quan đến các hoạt động bao gồm blob nén hoặc bản vá nhị phân trong các ví dụ này.
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 sẽ 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 sau 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, quá trình cập nhật sẽ không thành công và được thử lại với một tải trọng khác. Nếu tất cả các bước cho đến nay đều thành công thì quá trình cập nhật thành công và bước cuối cùng được thực thi.
7 Khe chưa sử dụng được đánh dấu là đang hoạt động bằng cách gọi setActiveBootSlot() . Đánh dấu khe không sử dụng là đang hoạt động không có nghĩa là nó sẽ khởi động xong. Bộ nạp khởi động (hoặc chính hệ thống) có thể chuyển khe 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 quay lại với mã thoát 0 ; nếu không thì 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í đích") đượ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 đã xác định bước cài đặt sau, 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 liên quan đến phân vùng được gắn. Ví dụ: nếu chương trình sau 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ừ vị trí không được 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, kernel cũ phải có khả năng:

  • Gắn kết đị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ó sự hỗ trợ cho nó trong kernel 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 kernel cũ (ví dụ: chương trình mới 64 bit chạy trên kernel 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 cài đặt được diễn giải bởi hệ nhị phân shell của hệ thống cũ với #! đá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 cài đặt sau nhị phân phức tạp hơn. Ngoài ra, bạn có thể chạy bước sau 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 vấn đề tương thích ngược hoặc các bản cập nhật bước đệm; đ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 gốc.

Chương trình sau 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 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ụ cần nỗ lực cao nhất khác (tức là cập nhật chương trình cơ sở hoặc bộ tải khởi động có khả năng A/B, chuẩn bị bản sao cơ sở dữ liệu cho phiên bản mới, v.v. ). Bước sau cài đặt không phù hợp để 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.

Chương trình sau khi cài đặt đã chọn sẽ 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 đó. Những 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 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 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ộ nạp khởi động và kernel cũng có thể kích hoạt khởi động lại nếu boot hoặc dm-verity đã được xác minh phát hiện bất kỳ lỗi nào. Sau khi quá trình kiểm tra hoàn tất, update_verifier đánh dấu quá trình 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 OTA A/B 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.