Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.

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 làm 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 thay thế thiết bị và thay thế thiết bị tại các trung tâm bảo hành và sửa chữa sẽ ít hơn. 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 cung cấp các 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 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, khởi động lại không mất nhiều thời gian hơn so với 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 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ử cập nhật lại miễn phí.
  • 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 không sử dụng và có thể được thử lại. Những lỗi như vậy cũng trở nên ít xảy ra hơn vì tải I / O cố tình 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 đến 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 nó. Truyền trực tuyến 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ộ nhớ cache 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ộ nhớ cache đủ 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ị gián đoạn. Nếu thiết bị không khởi động được do sự cố OTA hoặc dm-verity không hợp lệ, thiết bị có thể khởi động lại thành một hình ảnh cũ. (Android Verified Boot không yêu cầu cập nhật A / B.)

Giới thiệu về các bản cập nhật hệ thống A / B

Các bản 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 sẽ không yêu cầu thay đổi: các gói cập nhật vẫn được phân phối 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 hệ thống đều nằm trong AOSP và mã khách hàng đượ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 cho ứng dụng khách của chính 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 cập nhật. Vì các bản 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 khởi tạo nữa. Để tránh làm gián đoạ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ì nhàn rỗi, 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 các 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 chủ yếu giống với mã khách hàng hiện có 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 bây giờ để 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ử gói cập nhật 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 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 không thành công 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ẽ thông báo cho bootloader khởi động vào HĐH mới trong lần khởi động lại tiếp theo. Bộ nạp khởi động sẽ 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 đó không cần ứng dụng khách làm việc. 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 đủ.

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 chính sách mà 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 khách hàng 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 bả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 liệu 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 (vị trí), trình nền update_engine và các tương tác của bộ nạp khởi động (được mô tả bên dưới)
  • Xây dựng quy trình 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 (vị trí)

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à khe cắm (thường là khe A và khe 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 sẽ không được hệ thống truy cập 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ùng không sử dụng làm dự phòng: Nếu lỗi xảy ra trong hoặc ngay sau khi cập nhật, hệ thống có thể khôi phục lại vùng cũ và tiếp tục có 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ác phân vùng chỉ có một bản sao).

Mỗi vị trí có một thuộc tính khả năng khởi động cho biết vị trí đó có chứa hệ thống chính xác mà từ đó thiết bị có thể khởi động hay không. Vị trí hiện tại có thể khởi động được khi hệ thống đang chạy, nhưng vị trí khác 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 (vị trí mà bộ nạp khởi động sẽ khởi động vào 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, chỉ có liên quan nếu vị trí đó cũng có thể khởi động được. Khe cắm thành công sẽ có thể tự khởi động, chạy và cập nhật. Khe 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ừ đó) 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 đang hoạt động sang một khe có thể khởi động khác (thường là khe 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 công cụ

Các bản cập nhật hệ thống A / B sử dụng một daemon nền được gọi là 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ừ phân vùng A / B rãnh hiện tại và ghi bất kỳ dữ liệu nào vào phân vùng A / B rãnh không sử dụng theo hướng dẫn của gói OTA.
  • Gọi giao diện boot_control trong một 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 không sử dụng, theo hướng dẫn của gói OTA. (Để biết thêm chi tiết, hãy xem phần Hậu cài đặt ).

Vì daemon update_engine không tham gia vào chính 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 của SELinux trong vị trí hiện tại (các chính sách và tính năng như vậy không thể được cập nhật 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ùng 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 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 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 với bộ nạp khởi động

boot_control HAL được update_engine (và có thể là các trình daemon khác) sử dụng để hướng dẫn bộ nạp khởi động phải 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 những điều sau:

  • 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. Không có bản cập nhật nào được áp dụng cho đến nay. Khe hiện tại của hệ thống có thể 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ừ khe B, vì vậy khe B là khe có thể khởi động, thành công và đang hoạt động. Vị trí A được đánh dấu là không thể khởi động vì nội dung của vị trí 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 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 vẫn chưa được đánh dấu là thành công và một số lần thử khởi động từ khe A sẽ được thực hiện bởi bộ nạp khởi động.
  • Hệ thống khởi động lại thành bản cập nhật mới : Hệ thống đang chạy lần đầu tiên từ khe A, khe B vẫn khởi động được và thành công trong khi khe A chỉ có thể khởi động và vẫn hoạt động nhưng không thành công. Daemon không gian người dùng, update_verifier , nên đá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 /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ợ 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 . Phát 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ớ cho khoảng 100 KiB siêu dữ liệu.

Để bật cập nhậ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.

Vòng đời 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, 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, siêu dữ liệu trong chính gói OTA cho biết bản cập nhật có thể được truyền trực tuyến; cùng một gói 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 để cho máy khách biết nó đang phát trực tuyến, do đó máy khách sẽ 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 đang phát trực tuyến) và ứng dụ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ể sử dụng thực tế là gói thuộc biến thể phát trực tuyến để gửi cờ cho khách hàng để kích hoạt chuyển sang phía khung làm việc phát trực tuyến.

Sau khi có tải trọng, quá trình cập nhật như sau:

Bươc 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í đí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 bộ nạp khởi động rơi trở lại khe không sử dụng, nơi 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, 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 để khắc phục 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 các 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 những điều sau:
  • Siêu dữ liệu . Một phần tương đối nhỏ của trọng tải cập nhật, siêu dữ liệu chứa danh sách các hoạt động để tạo và xác minh phiên bản mới trên vị trí đích. Ví dụ: một hoạt động có thể giải nén một đốm màu 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 một số khối nhất định trong phân vùng đích.
  • Dữ liệu bổ sung . Là phần lớn của tải trọng cập nhật, dữ liệu bổ sung liên quan đến các hoạt động bao gồm khối nén hoặc bản vá nhị phân trong các ví dụ này.
3 Siêu dữ liệu trọng tải đượ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ớ được liên kết sẽ bị loại bỏ.
5 Toàn bộ phân vùng được đọc lại và xác minh theo hàm băm mong đợi.
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, bản cập nhật không thành công và được thử lại với tải trọng có thể khác. Nếu tất cả các bước cho đến nay đã thành công, thì bản cập nhật sẽ thành công và bước cuối cùng được thực hiện.
7 Khe 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 không sử dụng là hoạt động không có nghĩa là nó sẽ hoàn tất quá trình khởi động. Bộ nạp 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 trạng thái thành công.
số 8 Hậu cài đặt (mô tả bên dưới) liên quan đến việc chạy 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 định nghĩa trong gói OTA, bước này là bắt buộc và chương trình phải trả về với 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 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 mà bước sau cài đặt được xác định, update_engine 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 kết. Ví dụ: nếu chương trình sau cài đặt được định nghĩa là usr/bin/postinstall trong phân vùng hệ thống, thì phân vùng từ vị trí không sử dụng này 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 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 mới . Loại hệ thống tệp không thể thay đổi trừ khi có hỗ trợ cho nó trong hạt 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 cài đặt của phân vùng mới . Nếu sử dụng tệp nhị phân có thể thực thi và định dạng liên kết (ELF), nó phải tương thích với hạt 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-bit 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 một 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 thông dịch bởi hệ nhị phân shell của hệ thống cũ bằng dấu #! đánh dấu ở trên cùng), sau đó thiết lập các đường dẫn thư viện từ môi trường mới để thực thi một chương trình sau cài đặt 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 nhỏ hơn chuyên dụng để 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ậ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ộ nạp khởi động hỗ trợ 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 được.

Chương trình sau cài đặt đã chọn chạy trong ngữ cảnh postinstall SELinux. Tất cả các tệp trong phân vùng được gắn kết 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 các thuộc tính SELinux trong hệ thống mới sẽ không ảnh hưởng đến bước sau cài đặt. Nếu chương trình sau cài đặt cần các quyền bổ sung, các 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 sẽ kích hoạt kiểm tra tính toàn vẹn bằng cách sử dụng dm-verity. Việc 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 cản việc khôi phục an toàn. Trong quá trình này, bộ nạp khởi động và hạt 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 đã trích xuất sau khi hệ thống khởi động thành công vào phiên bản mới.