Các thay đổi về hành vi của Android 7.0

Cùng với các tính năng và chức năng mới, Android 7.0 còn có nhiều thay đổi về hành vi của hệ thống và API. Tài liệu này nêu bật một số thay đổi chính mà bạn nên hiểu và tính đến trong ứng dụng của mình.

Nếu bạn từng phát hành một ứng dụng dành cho Android, hãy lưu ý rằng những thay đổi này trên nền tảng có thể ảnh hưởng đến ứng dụng của bạn.

Pin và bộ nhớ

Android 7.0 có các thay đổi về hành vi của hệ thống nhằm cải thiện thời lượng pin của thiết bị và giảm mức sử dụng RAM. Những thay đổi này có thể ảnh hưởng đến quyền truy cập của ứng dụng vào tài nguyên hệ thống, cũng như cách ứng dụng tương tác với ứng dụng khác thông qua một số ý định ngầm ẩn nhất định.

Nghỉ

Ra mắt trong Android 6.0 (API cấp 23), chế độ Ngủ giúp cải thiện thời lượng pin bằng cách trì hoãn các hoạt động của CPU và mạng khi người dùng rút phích cắm thiết bị, để thiết bị ở trạng thái tĩnh và tắt màn hình. Android 7.0 cải tiến thêm chế độ Ngủ bằng cách áp dụng một tập hợp con các quy tắc hạn chế về CPU và mạng trong khi thiết bị đang rút phích cắm và tắt màn hình, nhưng không nhất thiết phải ở trạng thái tĩnh, ví dụ: khi điện thoại di động di chuyển trong túi của người dùng.

Hình minh hoạ cách chế độ Ngủ áp dụng mức hạn chế hoạt động hệ thống cấp đầu tiên để cải thiện thời lượng pin

Hình 1. Hình minh hoạ cách chế độ Ngủ áp dụng cấp độ hạn chế đầu tiên đối với hoạt động của hệ thống để cải thiện thời lượng pin.

Khi thiết bị đang chạy bằng pin và màn hình đã tắt trong một khoảng thời gian nhất định, thiết bị sẽ chuyển sang Chế độ nghỉ và áp dụng tập hợp con hạn chế đầu tiên: Tắt quyền truy cập mạng của ứng dụng, đồng thời trì hoãn các công việc và quá trình đồng bộ hoá. Nếu thiết bị đứng yên trong một khoảng thời gian nhất định sau khi chuyển sang Chế độ nghỉ, thì hệ thống sẽ áp dụng các hạn chế còn lại của Chế độ nghỉ cho PowerManager.WakeLock, chuông báo AlarmManager, GPS và tính năng quét Wi-Fi. Bất kể một số hay tất cả các hạn chế của chế độ Ngủ đang được áp dụng, hệ thống sẽ đánh thức thiết bị trong các khoảng thời gian bảo trì ngắn, trong đó các ứng dụng được phép truy cập mạng và có thể thực thi mọi công việc/hoạt động đồng bộ hoá bị trì hoãn.

Hình minh hoạ cách Chế độ nghỉ áp dụng cấp độ thứ hai của các quy định hạn chế hoạt động hệ thống sau khi thiết bị đứng yên trong một khoảng thời gian nhất định

Hình 2. Hình minh hoạ cách Chế độ nghỉ áp dụng cấp hạn chế thứ hai đối với hoạt động hệ thống sau khi thiết bị đứng yên trong một khoảng thời gian nhất định.

Xin lưu ý rằng việc bật màn hình hoặc cắm thiết bị sẽ thoát khỏi chế độ Ngủ và loại bỏ các hạn chế xử lý này. Hành vi bổ sung này không ảnh hưởng đến các đề xuất và phương pháp hay nhất để điều chỉnh ứng dụng của bạn cho phù hợp với phiên bản Doze trước đó được giới thiệu trong Android 6.0 (API cấp 23), như đã thảo luận trong phần Tối ưu hoá cho chế độ Nghỉ và Chế độ chờ ứng dụng. Bạn vẫn nên làm theo các đề xuất đó, chẳng hạn như sử dụng Giải pháp gửi thông báo qua đám mây của Firebase (FCM) để gửi và nhận thông báo, đồng thời bắt đầu lên kế hoạch cập nhật để phù hợp với hành vi Tiết kiệm pin bổ sung.

Project Svelte: Tối ưu hoá ở chế độ nền

Android 7.0 xoá 3 thông báo truyền phát ngầm để giúp tối ưu hoá cả mức sử dụng bộ nhớ và mức tiêu thụ điện năng. Thay đổi này là cần thiết vì các thông báo truyền tin ngầm thường khởi động các ứng dụng đã đăng ký để nghe thông báo ở chế độ nền. Việc xoá những thông báo này có thể giúp ích đáng kể cho hiệu suất và trải nghiệm người dùng của thiết bị.

Thiết bị di động thường xuyên thay đổi khả năng kết nối, chẳng hạn như khi chuyển đổi giữa Wi-Fi và dữ liệu di động. Hiện tại, các ứng dụng có thể theo dõi những thay đổi về khả năng kết nối bằng cách đăng ký dịch vụ nhận cho thông báo CONNECTIVITY_ACTION ngầm ẩn trong tệp kê khai. Vì nhiều ứng dụng đăng ký nhận thông báo truyền tin này, nên một lần chuyển đổi mạng có thể khiến tất cả ứng dụng đó thức dậy và xử lý thông báo truyền tin cùng một lúc.

Tương tự, trong các phiên bản Android trước, ứng dụng có thể đăng ký nhận thông báo truyền tin ACTION_NEW_PICTUREACTION_NEW_VIDEO ngầm ẩn từ các ứng dụng khác, chẳng hạn như Camera. Khi người dùng chụp ảnh bằng ứng dụng Máy ảnh, các ứng dụng này sẽ thức để xử lý thông báo truyền tin.

Để khắc phục những vấn đề này, Android 7.0 áp dụng các điểm tối ưu hoá sau:

  • Các ứng dụng nhắm mục tiêu Android 7.0 (API cấp 24) trở lên sẽ không nhận được thông báo CONNECTIVITY_ACTION nếu những ứng dụng đó khai báo trình nhận truyền phát trong tệp kê khai. Các ứng dụng vẫn sẽ nhận được thông báo CONNECTIVITY_ACTION nếu đăng ký BroadcastReceiver thông qua Context.registerReceiver() và ngữ cảnh đó vẫn hợp lệ.
  • Hệ thống không còn gửi thông báo ACTION_NEW_PICTURE hoặc ACTION_NEW_VIDEO nữa. Tính năng tối ưu hoá này ảnh hưởng đến tất cả ứng dụng chứ không chỉ những ứng dụng nhắm đến Android 7.0.

Nếu ứng dụng của bạn sử dụng các ý định này, thì bạn nên xoá các phần phụ thuộc này càng sớm càng tốt để có thể nhắm mục tiêu đúng cách trên những thiết bị chạy Android 7.0. Khung Android cung cấp một số giải pháp để giảm thiểu nhu cầu đối với các thông báo truyền phát ngầm này. Ví dụ: API JobScheduler cung cấp một cơ chế mạnh mẽ để lên lịch hoạt động mạng khi đáp ứng các điều kiện đã chỉ định, chẳng hạn như kết nối với mạng không đo lượng dữ liệu. Bạn thậm chí có thể sử dụng JobScheduler để phản ứng với các thay đổi đối với trình cung cấp nội dung.

Để biết thêm thông tin về tính năng tối ưu hoá ở chế độ nền trong Android 7.0 (API cấp 24) và cách điều chỉnh ứng dụng, hãy xem nội dung Tối ưu hoá ở chế độ nền.

Thay đổi về quyền

Android 7.0 có các thay đổi về quyền có thể ảnh hưởng đến ứng dụng của bạn.

Thay đổi quyền đối với hệ thống tệp

Để cải thiện tính bảo mật của các tệp riêng tư, thư mục riêng tư của các ứng dụng nhắm đến Android 7.0 trở lên đã hạn chế quyền truy cập (0700). Chế độ cài đặt này ngăn chặn việc rò rỉ siêu dữ liệu của các tệp riêng tư, chẳng hạn như kích thước hoặc sự tồn tại của các tệp đó. Thay đổi về quyền này có nhiều tác dụng phụ:

  • Chủ sở hữu không được nới lỏng quyền đối với tệp riêng tư nữa, và nếu cố gắng làm như vậy bằng MODE_WORLD_READABLE và/hoặc MODE_WORLD_WRITEABLE, thì sẽ kích hoạt SecurityException.

    Lưu ý: Cho đến nay, quy định hạn chế này chưa được thực thi đầy đủ. Các ứng dụng vẫn có thể sửa đổi quyền đối với thư mục riêng bằng cách sử dụng API gốc hoặc API File. Tuy nhiên, bạn không nên nới lỏng quyền đối với thư mục riêng tư.

  • Việc truyền URI file:// bên ngoài miền gói có thể khiến trình thu nhận có đường dẫn không truy cập được. Do đó, các nỗ lực truyền URI file:// sẽ kích hoạt FileUriExposedException. Bạn nên sử dụng FileProvider để chia sẻ nội dung của tệp riêng tư.
  • DownloadManager không thể chia sẻ các tệp được lưu trữ riêng tư theo tên tệp nữa. Các ứng dụng cũ có thể kết thúc bằng một đường dẫn không truy cập được khi truy cập vào COLUMN_LOCAL_FILENAME. Các ứng dụng nhắm đến Android 7.0 trở lên sẽ kích hoạt SecurityException khi cố gắng truy cập vào COLUMN_LOCAL_FILENAME. Các ứng dụng cũ đặt vị trí tải xuống thành vị trí công khai bằng cách sử dụng DownloadManager.Request.setDestinationInExternalFilesDir() hoặc DownloadManager.Request.setDestinationInExternalPublicDir() vẫn có thể truy cập vào đường dẫn trong COLUMN_LOCAL_FILENAME, tuy nhiên, bạn không nên sử dụng phương thức này. Cách ưu tiên để truy cập vào tệp do DownloadManager hiển thị là sử dụng ContentResolver.openFileDescriptor().

Chia sẻ tệp giữa các ứng dụng

Đối với các ứng dụng nhắm đến Android 7.0, khung Android sẽ thực thi chính sách API StrictMode, trong đó nghiêm cấm hiển thị URI file:// bên ngoài ứng dụng. Nếu một ý định chứa URI tệp rời khỏi ứng dụng, thì ứng dụng sẽ không thành công với ngoại lệ FileUriExposedException.

Để chia sẻ tệp giữa các ứng dụng, bạn nên gửi URI content:// và cấp quyền truy cập tạm thời trên URI đó. Cách dễ nhất để cấp quyền này là sử dụng lớp FileProvider. Để biết thêm thông tin về quyền và cách chia sẻ tệp, hãy xem bài viết Chia sẻ tệp.

Cải tiến khả năng hỗ trợ tiếp cận

Android 7.0 có các thay đổi nhằm cải thiện khả năng hữu dụng của nền tảng cho người dùng có thị lực kém hoặc khiếm thị. Thường thì những thay đổi này sẽ không đòi hỏi bạn thay đổi mã trong ứng dụng. Tuy nhiên, bạn nên xem xét và kiểm thử những tính năng này trong ứng dụng để đánh giá những tác động tiềm ẩn đối với trải nghiệm người dùng.

Thu phóng màn hình

Android 7.0 cho phép người dùng đặt Kích thước màn hình để phóng to hoặc thu nhỏ tất cả các thành phần trên màn hình, từ đó cải thiện khả năng hỗ trợ tiếp cận của thiết bị cho người dùng có thị lực kém. Người dùng không thể thu phóng màn hình quá chiều rộng màn hình tối thiểu là sw320dp. Đây là chiều rộng của Nexus 4 (một loại điện thoại có kích thước trung bình phổ biến).

Màn hình hiển thị kích thước màn hình chưa phóng to của thiết bị chạy hình ảnh hệ thống Android 7.0
Màn hình cho thấy tác động của việc tăng kích thước hiển thị của thiết bị chạy ảnh hệ thống Android 7.0

Hình 3. Màn hình ở bên phải cho thấy hiệu quả của việc tăng Kích thước hiển thị của một thiết bị chạy hình ảnh hệ thống Android 7.0.

Khi mật độ thiết bị thay đổi, hệ thống sẽ thông báo cho các ứng dụng đang chạy theo các cách sau:

  • Nếu một ứng dụng nhắm đến API cấp 23 trở xuống, thì hệ thống sẽ tự động tắt tất cả các quy trình ở chế độ nền của ứng dụng đó. Điều này có nghĩa là nếu người dùng chuyển khỏi một ứng dụng như vậy để mở màn hình Cài đặt và thay đổi chế độ cài đặt Kích thước hiển thị, thì hệ thống sẽ tắt ứng dụng đó theo cách tương tự như khi bộ nhớ thấp. Nếu ứng dụng có bất kỳ quy trình nào ở nền trước, hệ thống sẽ thông báo cho các quy trình đó về thay đổi cấu hình như mô tả trong phần Xử lý các thay đổi về thời gian chạy, giống như khi hướng của thiết bị thay đổi.
  • Nếu một ứng dụng nhắm đến Android 7.0, thì tất cả các quy trình của ứng dụng đó (nền trước và nền sau) sẽ được thông báo về thay đổi về cấu hình như mô tả trong phần Xử lý các thay đổi về thời gian chạy.

Hầu hết ứng dụng không cần thực hiện bất kỳ thay đổi nào để hỗ trợ tính năng này, miễn là các ứng dụng đó tuân thủ các phương pháp hay nhất của Android. Những điều cụ thể cần kiểm tra:

  • Hãy kiểm thử ứng dụng của bạn trên một thiết bị có chiều rộng màn hình là sw320dp và đảm bảo ứng dụng đó hoạt động như mong muốn.
  • Khi cấu hình thiết bị thay đổi, hãy cập nhật mọi thông tin được lưu vào bộ nhớ đệm phụ thuộc vào mật độ, chẳng hạn như bitmap hoặc tài nguyên được lưu vào bộ nhớ đệm được tải từ mạng. Kiểm tra các thay đổi về cấu hình khi ứng dụng tiếp tục từ trạng thái tạm dừng.

    Lưu ý: Nếu bạn lưu dữ liệu phụ thuộc vào cấu hình vào bộ nhớ đệm, bạn nên đưa siêu dữ liệu liên quan vào, chẳng hạn như kích thước màn hình hoặc mật độ điểm ảnh phù hợp cho dữ liệu đó. Việc lưu siêu dữ liệu này cho phép bạn quyết định xem có cần làm mới dữ liệu đã lưu vào bộ nhớ đệm sau khi thay đổi cấu hình hay không.

  • Tránh chỉ định kích thước bằng đơn vị px, vì các kích thước này không điều chỉnh theo mật độ màn hình. Thay vào đó, hãy chỉ định kích thước bằng đơn vị pixel không phụ thuộc vào mật độ (dp).

Cài đặt hỗ trợ thị giác trong Trình hướng dẫn thiết lập

Android 7.0 có Chế độ cài đặt thị giác trên màn hình Chào mừng, nơi người dùng có thể thiết lập các chế độ cài đặt hỗ trợ tiếp cận sau đây trên thiết bị mới: Cử chỉ phóng to, Kích thước phông chữ, Kích thước hiển thịTalkBack. Thay đổi này giúp tăng khả năng hiển thị lỗi liên quan đến các chế độ cài đặt màn hình. Để đánh giá tác động của tính năng này, bạn nên kiểm thử ứng dụng của mình khi bật các chế độ cài đặt này. Bạn có thể tìm thấy chế độ cài đặt này trong phần Cài đặt > Hỗ trợ tiếp cận.

Liên kết ứng dụng NDK với thư viện nền tảng

Kể từ Android 7.0, hệ thống sẽ ngăn các ứng dụng liên kết động với các thư viện không phải NDK. Điều này có thể khiến ứng dụng của bạn gặp sự cố. Thay đổi về hành vi này nhằm tạo ra trải nghiệm ứng dụng nhất quán trên các bản cập nhật nền tảng và nhiều thiết bị. Mặc dù mã của bạn có thể không liên kết với thư viện riêng tư, nhưng thư viện tĩnh của bên thứ ba trong ứng dụng có thể đang thực hiện việc này. Do đó, tất cả nhà phát triển đều nên kiểm tra để đảm bảo ứng dụng của họ không gặp sự cố trên các thiết bị chạy Android 7.0. Nếu ứng dụng của bạn sử dụng mã gốc, bạn chỉ nên sử dụng API NDK công khai.

Có 3 cách ứng dụng của bạn có thể đang cố gắng truy cập vào API nền tảng riêng tư:

  • Ứng dụng của bạn truy cập trực tiếp vào các thư viện nền tảng riêng tư. Bạn nên cập nhật ứng dụng để đưa vào bản sao của các thư viện đó hoặc sử dụng API NDK công khai.
  • Ứng dụng của bạn sử dụng một thư viện bên thứ ba truy cập vào thư viện nền tảng riêng tư. Ngay cả khi chắc chắn rằng ứng dụng của bạn không truy cập trực tiếp vào thư viện riêng tư, bạn vẫn nên kiểm thử ứng dụng cho trường hợp này.
  • Ứng dụng của bạn tham chiếu đến một thư viện không có trong tệp APK. Ví dụ: trường hợp này có thể xảy ra nếu bạn cố sử dụng bản sao của OpenSSL riêng nhưng lại quên đóng gói bản sao đó với APK của ứng dụng. Ứng dụng có thể chạy bình thường trên các phiên bản nền tảng Android có chứa libcrypto.so. Tuy nhiên, ứng dụng có thể gặp sự cố trên các phiên bản Android sau này không bao gồm thư viện này (chẳng hạn như Android 6.0 trở lên). Để khắc phục vấn đề này, hãy đảm bảo rằng bạn gói tất cả thư viện không phải NDK với APK.

Ứng dụng không nên sử dụng các thư viện gốc không có trong NDK vì các thư viện này có thể thay đổi hoặc bị xoá giữa các phiên bản Android. Việc chuyển từ OpenSSL sang BoringSSL là một ví dụ về thay đổi như vậy. Ngoài ra, vì không có yêu cầu về khả năng tương thích đối với các thư viện nền tảng không có trong NDK, nên các thiết bị có thể cung cấp các mức độ tương thích khác nhau.

Để giảm tác động mà quy định hạn chế này có thể gây ra cho các ứng dụng hiện đã phát hành, một nhóm thư viện có mức sử dụng đáng kể (chẳng hạn như libandroid_runtime.so, libcutils.so, libcrypto.solibssl.so) sẽ tạm thời có thể truy cập được trên Android 7.0 (API cấp 24) đối với các ứng dụng nhắm đến API cấp 23 trở xuống. Nếu ứng dụng của bạn tải một trong các thư viện này, logcat sẽ tạo một cảnh báo và một thông báo ngắn sẽ xuất hiện trên thiết bị mục tiêu để thông báo cho bạn. Nếu thấy các cảnh báo này, bạn nên cập nhật ứng dụng để đưa vào bản sao của riêng các thư viện đó hoặc chỉ sử dụng các API NDK công khai. Các bản phát hành trong tương lai của nền tảng Android có thể hạn chế hoàn toàn việc sử dụng thư viện riêng tư và khiến ứng dụng của bạn gặp sự cố.

Tất cả ứng dụng đều tạo lỗi thời gian chạy khi gọi một API không công khai và không truy cập được tạm thời. Kết quả là cả System.loadLibrarydlopen(3) đều trả về NULL và có thể khiến ứng dụng của bạn gặp sự cố. Bạn nên xem lại mã ứng dụng để xoá việc sử dụng các API nền tảng riêng tư và kiểm thử kỹ lưỡng ứng dụng bằng một thiết bị hoặc trình mô phỏng chạy Android 7.0 (API cấp 24). Nếu không chắc ứng dụng của mình có sử dụng thư viện riêng tư hay không, bạn có thể kiểm tra logcat để xác định lỗi thời gian chạy.

Bảng sau đây mô tả hành vi mà bạn có thể thấy từ một ứng dụng, tuỳ thuộc vào việc ứng dụng đó sử dụng thư viện gốc riêng tư và cấp độ API mục tiêu (android:targetSdkVersion).

Thư viện Cấp API mục tiêu Quyền truy cập vào thời gian chạy thông qua trình liên kết động Hành vi của Android 7.0 (API cấp 24) Hành vi trong tương lai của nền tảng Android
NDK công khai Khán giả có Dễ tiếp cận Hoạt động như mong đợi Hoạt động như mong đợi
Riêng tư (thư viện riêng tư có thể truy cập tạm thời) 23 trở xuống Tạm thời có thể sử dụng Hoạt động như mong đợi, nhưng bạn sẽ nhận được cảnh báo logcat. Lỗi thời gian chạy
Riêng tư (thư viện riêng tư có thể truy cập tạm thời) 24 trở lên Bị hạn chế Lỗi thời gian chạy Lỗi thời gian chạy
Riêng tư (khác) Khán giả có Bị hạn chế Lỗi thời gian chạy Lỗi thời gian chạy

Kiểm tra xem ứng dụng của bạn có sử dụng thư viện riêng tư hay không

Để giúp bạn xác định sự cố khi tải thư viện riêng tư, logcat có thể tạo ra cảnh báo hoặc lỗi thời gian chạy. Ví dụ: nếu ứng dụng của bạn nhắm đến API cấp 23 trở xuống và cố gắng truy cập vào một thư viện riêng tư trên thiết bị chạy Android 7.0, thì bạn có thể thấy cảnh báo tương tự như sau:

03-21 17:07:51.502 31234 31234 W linker  : library "libandroid_runtime.so"
("/system/lib/libandroid_runtime.so") needed or dlopened by
"/data/app/com.popular-app.android-2/lib/arm/libapplib.so" is not accessible
for the namespace "classloader-namespace" - the access is temporarily granted
as a workaround for http://b/26394120

Các cảnh báo logcat này cho bạn biết thư viện nào đang cố gắng truy cập vào API nền tảng riêng tư, nhưng sẽ không khiến ứng dụng của bạn gặp sự cố. Tuy nhiên, nếu ứng dụng nhắm đến API cấp 24 trở lên, logcat sẽ tạo lỗi thời gian chạy sau đây và ứng dụng của bạn có thể gặp sự cố:

java.lang.UnsatisfiedLinkError: dlopen failed: library "libcutils.so"
("/system/lib/libcutils.so") needed or dlopened by
"/system/lib/libnativeloader.so" is not accessible for the namespace
"classloader-namespace"
  at java.lang.Runtime.loadLibrary0(Runtime.java:977)
  at java.lang.System.loadLibrary(System.java:1602)

Bạn cũng có thể thấy các kết quả logcat này nếu ứng dụng của bạn sử dụng các thư viện bên thứ ba liên kết linh động với API nền tảng riêng tư. Công cụ readelf trong Android 7.0DK cho phép bạn tạo danh sách tất cả thư viện dùng chung được liên kết động của một tệp .so nhất định bằng cách chạy lệnh sau:

aarch64-linux-android-readelf -dW libMyLibrary.so

Cập nhật ứng dụng

Dưới đây là một số bước bạn có thể thực hiện để khắc phục các loại lỗi này và đảm bảo ứng dụng không gặp sự cố khi cập nhật nền tảng trong tương lai:

  • Nếu ứng dụng của bạn sử dụng thư viện nền tảng riêng tư, bạn nên cập nhật ứng dụng đó để đưa vào bản sao riêng của các thư viện đó hoặc sử dụng API NDK công khai.
  • Nếu ứng dụng của bạn sử dụng thư viện bên thứ ba có quyền truy cập vào các biểu tượng riêng tư, hãy liên hệ với tác giả thư viện để cập nhật thư viện.
  • Hãy nhớ đóng gói tất cả thư viện không phải NDK bằng APK.
  • Sử dụng các hàm JNI tiêu chuẩn thay vì getJavaVMgetJNIEnv từ libandroid_runtime.so:
    AndroidRuntime::getJavaVM -> GetJavaVM from <jni.h>
    AndroidRuntime::getJNIEnv -> JavaVM::GetEnv or
    JavaVM::AttachCurrentThread from <jni.h>.
    
  • Sử dụng __system_property_get thay vì ký hiệu property_get riêng tư từ libcutils.so. Để thực hiện việc này, hãy sử dụng __system_property_get với các hàm sau:
    #include <sys/system_properties.h>

    Lưu ý: Tính sẵn có và nội dung của các thuộc tính hệ thống không được kiểm thử thông qua CTS. Cách khắc phục tốt hơn là tránh sử dụng tất cả các thuộc tính này.

  • Sử dụng phiên bản cục bộ của ký hiệu SSL_ctrl từ libcrypto.so. Ví dụ: bạn nên liên kết tĩnh libcyrpto.a trong tệp .so hoặc bao gồm một phiên bản libcrypto.so được liên kết động từ BoringSSL/OpenSSL và đóng gói phiên bản này trong APK của bạn.

Android for Work

Android 7.0 có các thay đổi đối với các ứng dụng nhắm đến Android cho công việc, bao gồm cả các thay đổi đối với việc cài đặt chứng chỉ, đặt lại mật khẩu, quản lý người dùng phụ và quyền truy cập vào giá trị nhận dạng thiết bị. Nếu đang xây dựng ứng dụng cho môi trường Android cho công việc, bạn nên xem xét các thay đổi này và sửa đổi ứng dụng cho phù hợp.

  • Bạn phải cài đặt trình cài đặt chứng chỉ được uỷ quyền trước khi DPC có thể đặt trình cài đặt này. Đối với cả ứng dụng hồ sơ và ứng dụng của chủ sở hữu thiết bị nhắm đến Android 7.0 (API cấp 24), bạn nên cài đặt trình cài đặt chứng chỉ được uỷ quyền trước khi trình kiểm soát chính sách thiết bị (DPC) gọi DevicePolicyManager.setCertInstallerPackage(). Nếu trình cài đặt chưa được cài đặt, hệ thống sẽ gửi một IllegalArgumentException.
  • Giờ đây, các hạn chế về việc đặt lại mật khẩu đối với quản trị viên thiết bị sẽ áp dụng cho chủ sở hữu hồ sơ. Quản trị viên thiết bị không thể sử dụng DevicePolicyManager.resetPassword() để xoá mật khẩu hoặc thay đổi mật khẩu đã đặt nữa. Quản trị viên thiết bị vẫn có thể đặt mật khẩu, nhưng chỉ khi thiết bị không có mật khẩu, mã PIN hoặc hình mở khoá.
  • Chủ sở hữu thiết bị và hồ sơ có thể quản lý tài khoản ngay cả khi bạn đặt các quy định hạn chế. Chủ sở hữu thiết bị và chủ sở hữu hồ sơ có thể gọi Account Management API ngay cả khi có các quy định hạn chế đối với người dùng DISALLOW_MODIFY_ACCOUNTS.
  • Chủ sở hữu thiết bị có thể quản lý người dùng phụ dễ dàng hơn. Khi một thiết bị đang chạy ở chế độ chủ sở hữu thiết bị, hạn chế DISALLOW_ADD_USER sẽ tự động được đặt. Điều này ngăn người dùng tạo người dùng phụ không được quản lý. Ngoài ra, các phương thức CreateUser()createAndInitializeUser() không còn được dùng nữa; phương thức DevicePolicyManager.createAndManageUser() mới sẽ thay thế các phương thức này.
  • Chủ sở hữu thiết bị có thể truy cập vào giá trị nhận dạng thiết bị. Chủ sở hữu thiết bị có thể truy cập vào địa chỉ MAC Wi-Fi của một thiết bị bằng DevicePolicyManager.getWifiMacAddress(). Nếu Wi-Fi chưa bao giờ được bật trên thiết bị, thì phương thức này sẽ trả về giá trị null.
  • Cài đặt Chế độ công việc kiểm soát quyền truy cập vào các ứng dụng công việc. Khi chế độ làm việc tắt, trình chạy hệ thống sẽ cho biết các ứng dụng công việc không dùng được bằng cách chuyển sang màu xám. Việc bật lại chế độ làm việc sẽ khôi phục chế độ bình thường.
  • Khi cài đặt tệp PKCS #12 chứa chuỗi chứng chỉ ứng dụng và khoá riêng tư tương ứng từ giao diện người dùng Cài đặt, chứng chỉ CA trong chuỗi sẽ không còn được cài đặt vào bộ nhớ thông tin xác thực đáng tin cậy. Điều này không ảnh hưởng đến kết quả của KeyChain.getCertificateChain() khi các ứng dụng cố gắng truy xuất chuỗi chứng chỉ của ứng dụng sau này. Nếu cần, bạn phải cài đặt riêng chứng chỉ CA vào bộ nhớ thông tin xác thực đáng tin cậy thông qua Giao diện người dùng Cài đặt một cách riêng biệt, với định dạng được mã hoá DER dưới đuôi tệp .crt hoặc .cer.
  • Kể từ Android 7.0, việc đăng ký và lưu trữ vân tay được quản lý theo từng người dùng. Nếu Ứng dụng chính sách thiết bị (DPC) của chủ sở hữu hồ sơ nhắm đến API cấp 23 (trở xuống) trên một thiết bị chạy Android 7.0 (API cấp 24), thì người dùng vẫn có thể đặt vân tay trên thiết bị, nhưng các ứng dụng công việc không thể truy cập vào vân tay thiết bị. Khi DPC nhắm đến API cấp 24 trở lên, người dùng có thể đặt vân tay riêng cho hồ sơ công việc bằng cách chuyển đến phần Cài đặt > Bảo mật > Bảo mật hồ sơ công việc.
  • DevicePolicyManager.getStorageEncryptionStatus() sẽ trả về trạng thái mã hoá mới ENCRYPTION_STATUS_ACTIVE_PER_USER để cho biết rằng quá trình mã hoá đang hoạt động và khoá mã hoá được liên kết với người dùng. Trạng thái mới chỉ được trả về nếu DPC nhắm đến API cấp 24 trở lên. Đối với các ứng dụng nhắm đến các cấp độ API trước đó, ENCRYPTION_STATUS_ACTIVE sẽ được trả về, ngay cả khi khoá mã hoá là dành riêng cho người dùng hoặc hồ sơ.
  • Trong Android 7.0, một số phương thức thường ảnh hưởng đến toàn bộ thiết bị sẽ hoạt động theo cách khác nếu thiết bị đã cài đặt hồ sơ công việc với một thử thách công việc riêng biệt. Thay vì ảnh hưởng đến toàn bộ thiết bị, các phương thức này chỉ áp dụng cho hồ sơ công việc. (Danh sách đầy đủ các phương thức như vậy có trong tài liệu về DevicePolicyManager.getParentProfileInstance().) Ví dụ: DevicePolicyManager.lockNow() chỉ khoá hồ sơ công việc thay vì khoá toàn bộ thiết bị. Đối với mỗi phương thức này, bạn có thể lấy hành vi cũ bằng cách gọi phương thức trên thực thể mẹ của DevicePolicyManager; bạn có thể lấy thực thể mẹ này bằng cách gọi DevicePolicyManager.getParentProfileInstance(). Ví dụ: nếu bạn gọi phương thức lockNow() của thực thể mẹ, thì toàn bộ thiết bị sẽ bị khoá.

Tỷ lệ giữ chân chú thích

Android 7.0 khắc phục lỗi chế độ hiển thị của chú giải bị bỏ qua. Vấn đề này cho phép thời gian chạy truy cập vào các chú thích mà lẽ ra không thể truy cập được. Các chú thích này bao gồm:

  • VISIBILITY_BUILD: Chỉ hiển thị tại thời điểm tạo bản dựng.
  • VISIBILITY_SYSTEM: Nhằm hiển thị trong thời gian chạy, nhưng chỉ hiển thị với hệ thống cơ bản.

Nếu ứng dụng của bạn dựa vào hành vi này, vui lòng thêm chính sách giữ lại dữ liệu vào các chú giải phải có trong thời gian chạy. Bạn có thể thực hiện việc này bằng cách sử dụng @Retention(RetentionPolicy.RUNTIME).

Thay đổi về cấu hình mặc định của TLS/SSL

Android 7.0 thực hiện các thay đổi sau đối với cấu hình TLS/SSL mặc định mà các ứng dụng dùng cho HTTPS và lưu lượng truy cập TLS/SSL khác:

  • Bộ mật mã RC4 hiện đã bị tắt.
  • Bộ thuật toán mật mã CHACHA20-POLY1305 hiện đã được bật.

Việc RC4 bị tắt theo mặc định có thể dẫn đến sự cố kết nối HTTPS hoặc TLS/SSL khi máy chủ không đàm phán các bộ mật mã hiện đại. Cách khắc phục ưu tiên là cải thiện cấu hình của máy chủ để cho phép các giao thức và bộ thuật toán mật mã mạnh mẽ và hiện đại hơn. Tốt nhất là bạn nên bật TLSv1.2 và AES-GCM, đồng thời bật và ưu tiên sử dụng bộ thuật toán mật mã Forward Secrecy (ECDHE).

Một giải pháp thay thế là sửa đổi ứng dụng để sử dụng SSLSocketFactory tuỳ chỉnh nhằm giao tiếp với máy chủ. Bạn nên thiết kế nhà máy để tạo các thực thể SSLSocket có một số bộ mật mã mà máy chủ yêu cầu được bật ngoài các bộ mật mã mặc định.

Lưu ý: Những thay đổi này không liên quan đến WebView.

Ứng dụng nhắm đến Android 7.0

Những thay đổi về hành vi này chỉ áp dụng cho những ứng dụng nhắm đến Android 7.0 (API cấp 24) trở lên. Các ứng dụng biên dịch cho Android 7.0 hoặc đặt targetSdkVersion thành Android 7.0 trở lên phải sửa đổi ứng dụng của mình để hỗ trợ đúng cách những hành vi này (nếu ứng dụng có hỗ trợ).

Thay đổi về quá trình chuyển đổi tuần tự

Android 7.0 (API cấp 24) sửa một lỗi trong quá trình tính toán sê-ri sê-ri mặc định không khớp với thông số kỹ thuật.

Các lớp triển khai Serializable và không chỉ định trường serialVersionUID rõ ràng có thể thấy sự thay đổi trong serialVersionUID mặc định, điều này sẽ khiến ngoại lệ được gửi khi cố gắng chuyển đổi tuần tự các thực thể của lớp đã được chuyển đổi tuần tự trên một phiên bản cũ hơn hoặc được chuyển đổi tuần tự bằng một ứng dụng nhắm đến một phiên bản cũ hơn. Thông báo lỗi sẽ có dạng như sau:

local class incompatible: stream classdesc serialVersionUID = 1234, local class serialVersionUID = 4567

Để khắc phục các vấn đề này, bạn cần thêm trường serialVersionUID vào bất kỳ lớp nào bị ảnh hưởng có giá trị stream classdesc serialVersionUID từ thông báo lỗi, ví dụ: 1234 trong trường hợp này. Thay đổi đó tuân thủ tất cả các đề xuất về phương pháp hay để viết mã chuyển đổi tuần tự và sẽ hoạt động trên tất cả các phiên bản Android.

Lỗi cụ thể đã được khắc phục liên quan đến sự hiện diện của các phương thức khởi chạy tĩnh, tức là <clinit>. Theo thông số kỹ thuật, sự hiện diện hoặc không có của phương thức khởi động tĩnh trong lớp này sẽ ảnh hưởng đến sê-ri sê-ri mặc định được tính cho lớp đó. Trước khi khắc phục lỗi, phép tính cũng sẽ kiểm tra lớp mẹ để tìm trình khởi chạy tĩnh nếu lớp không có trình khởi chạy tĩnh.

Để làm rõ, thay đổi này không ảnh hưởng đến những ứng dụng nhắm đến API cấp 23 trở xuống, các lớp có trường serialVersionUID hoặc các lớp có phương thức khởi chạy tĩnh.

Các điểm quan trọng khác

  • Khi một ứng dụng đang chạy trên Android 7.0, nhưng nhắm đến cấp độ API thấp hơn và người dùng thay đổi kích thước màn hình, thì quy trình của ứng dụng sẽ bị tắt. Ứng dụng phải có khả năng xử lý linh hoạt trường hợp này. Nếu không, ứng dụng sẽ gặp sự cố khi người dùng khôi phục từ mục Gần đây.

    Bạn nên kiểm thử ứng dụng để đảm bảo hành vi này không xảy ra. Bạn có thể thực hiện việc này bằng cách gây ra một sự cố tương tự khi tắt ứng dụng theo cách thủ công thông qua DDMS.

    Các ứng dụng nhắm đến Android 7.0 (API cấp 24) trở lên sẽ không tự động bị tắt khi mật độ thay đổi; tuy nhiên, các ứng dụng này vẫn có thể phản hồi không tốt với các thay đổi về cấu hình.

  • Ứng dụng trên Android 7.0 phải có thể xử lý linh hoạt các thay đổi về cấu hình và không được gặp sự cố khi khởi động lại. Bạn có thể xác minh hành vi của ứng dụng bằng cách thay đổi kích thước phông chữ (Cài đặt > Màn hình > Kích thước phông chữ), sau đó khôi phục ứng dụng từ phần Gần đây.
  • Do một lỗi trong các phiên bản Android trước, hệ thống không gắn cờ việc ghi vào ổ cắm TCP trên luồng chính là lỗi vi phạm chế độ nghiêm ngặt. Android 7.0 đã khắc phục lỗi này. Các ứng dụng biểu hiện hành vi này hiện sẽ gửi một android.os.NetworkOnMainThreadException. Nhìn chung, bạn không nên thực hiện các thao tác mạng trên luồng chính vì các thao tác này thường có độ trễ cao, gây ra lỗi ANR và hiện tượng giật.
  • Nhóm phương thức Debug.startMethodTracing() hiện mặc định lưu trữ đầu ra trong thư mục dành riêng cho gói trên bộ nhớ dùng chung, thay vì ở cấp cao nhất của thẻ SD. Điều này có nghĩa là các ứng dụng không cần yêu cầu quyền WRITE_EXTERNAL_STORAGE để sử dụng các API này nữa.
  • Nhiều API nền tảng hiện đã bắt đầu kiểm tra các tải trọng lớn được gửi trong các giao dịch Binder. Ngoài ra, hệ thống hiện gửi lại TransactionTooLargeExceptions dưới dạng RuntimeExceptions, thay vì tự động ghi nhật ký hoặc chặn các tải trọng đó. Một ví dụ phổ biến là lưu trữ quá nhiều dữ liệu trong Activity.onSaveInstanceState(), khiến ActivityThread.StopInfo gửi RuntimeException khi ứng dụng của bạn nhắm đến Android 7.0.
  • Nếu một ứng dụng đăng tác vụ Runnable vào ViewView không được đính kèm vào cửa sổ, thì hệ thống sẽ đưa tác vụ Runnable vào hàng đợi với View; tác vụ Runnable sẽ không thực thi cho đến khi View được đính kèm vào cửa sổ. Hành vi này khắc phục các lỗi sau:
    • Nếu một ứng dụng được đăng lên View từ một luồng khác với luồng giao diện người dùng của cửa sổ dự kiến, thì Runnable có thể chạy trên luồng không chính xác.
    • Nếu tác vụ Runnable được đăng từ một luồng không phải là luồng của trình lặp, thì ứng dụng có thể hiển thị tác vụ Runnable.
  • Nếu một ứng dụng trên Android 7.0 có quyền DELETE_PACKAGES cố gắng xoá một gói, nhưng một ứng dụng khác đã cài đặt gói đó, thì hệ thống sẽ yêu cầu người dùng xác nhận. Trong trường hợp này, ứng dụng sẽ nhận được STATUS_PENDING_USER_ACTION làm trạng thái trả về khi gọi PackageInstaller.uninstall().
  • Nhà cung cấp JCA có tên là Crypto không còn được dùng nữa vì thuật toán duy nhất của nó, SHA1PRNG, có độ bảo mật yếu. Các ứng dụng không thể sử dụng SHA1PRNG để lấy khoá (không an toàn) nữa vì nhà cung cấp này không còn hoạt động nữa. Để biết thêm thông tin, hãy xem bài đăng trên blog Không dùng trình cung cấp "Crypto" bảo mật nữa trong Android N.