שינויים בהתנהגות: אפליקציות שמטרגטות ל-Android 14 ואילך

בדומה לגרסאות קודמות, Android 14 כולל שינויים בהתנהגות שיכולים להשפיע על האפליקציה שלכם. השינויים הבאים בהתנהגות חלים באופן בלעדי על אפליקציות שמטרגטות ל-Android 14 (רמת API 34) ומעלה. אם האפליקציה שלכם מטרגטת את Android 14 ואילך, עליכם לשנות את האפליקציה כך שתתמוך בהתנהגויות האלה בצורה נכונה, במקרים הרלוונטיים.

חשוב גם לעיין ברשימת השינויים בהתנהגות שמשפיעים על כל האפליקציות שפועלות ב-Android 14, בלי קשר לtargetSdkVersion של האפליקציה.

פונקציונליות עיקרית

חובה לציין את סוגי השירותים שפועלים בחזית

אם האפליקציה שלכם מטרגטת ל-Android 14 (רמת API ‏34) ואילך, צריך לציין לפחות סוג אחד של שירות שפועל בחזית לכל שירות שפועל בחזית באפליקציה. כדאי לבחור סוג של שירות שפועל בחזית שמייצג את תרחיש השימוש של האפליקציה. המערכת מצפה ששירותים שפועלים בחזית עם סוג מסוים יעמדו בדרישות של תרחיש לדוגמה מסוים.

אם תרחיש שימוש באפליקציה לא משויך לאף אחד מהסוגים האלה, מומלץ מאוד להעביר את הלוגיקה שלכם כך שתשתמש ב-WorkManager או במשימות להעברת נתונים שמבוצעות ביוזמת המשתמש.

אכיפה של ההרשאה BLUETOOTH_CONNECT ב-BluetoothAdapter

ב-Android 14 מתבצעת אכיפה של ההרשאה BLUETOOTH_CONNECT בקריאה לשיטה BluetoothAdaptergetProfileConnectionState() באפליקציות שמטרגטות ל-Android 14 (רמת API ‏34) ואילך.

השיטה הזו כבר דרשה את ההרשאה BLUETOOTH_CONNECT, אבל היא לא אוכפה. חשוב לוודא שבקובץ AndroidManifest.xml של האפליקציה מוצהר על השירות BLUETOOTH_CONNECT כפי שמוצג בקטע הקוד הבא, ולוודא שהמשתמש העניק את ההרשאה לפני הקריאה ל-getProfileConnectionState.

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

עדכונים ל-OpenJDK 17

Android 14 continues the work of refreshing Android's core libraries to align with the features in the latest OpenJDK LTS releases, including both library updates and Java 17 language support for app and platform developers.

A few of these changes can affect app compatibility:

  • Changes to regular expressions: Invalid group references are now disallowed to more closely follow the semantics of OpenJDK. You might see new cases where an IllegalArgumentException is thrown by the java.util.regex.Matcher class, so make sure to test your app for areas that use regular expressions. To enable or disable this change while testing, toggle the DISALLOW_INVALID_GROUP_REFERENCE flag using the compatibility framework tools.
  • UUID handling: The java.util.UUID.fromString() method now does more strict checks when validating the input argument, so you might see an IllegalArgumentException during deserialization. To enable or disable this change while testing, toggle the ENABLE_STRICT_VALIDATION flag using the compatibility framework tools.
  • ProGuard issues: In some cases, the addition of the java.lang.ClassValue class causes an issue if you try to shrink, obfuscate, and optimize your app using ProGuard. The problem originates with a Kotlin library that changes runtime behaviour based on whether Class.forName("java.lang.ClassValue") returns a class or not. If your app was developed against an older version of the runtime without the java.lang.ClassValue class available, then these optimizations might remove the computeValue method from classes derived from java.lang.ClassValue.

‫JobScheduler מחזק את ההתנהגות של קריאות חוזרות (callback) ושל הרשת

מאז ההשקה של התכונה JobScheduler, מצפה שהאפליקציה תחזור מ- onStartJob או onStopJob בתוך כמה שניות. לפני Android 14, אם משימה מסוימת נמשכת יותר מדי זמן, היא מופסקת ונכשלה באופן שקט. אם האפליקציה שלכם מטרגטת ל-Android 14 (רמת API 34) ואילך וחרגה מהזמן שהוקצה לשרשור הראשי, האפליקציה תפעיל אירוע ANR עם הודעת השגיאה 'No response to onStartJob' או 'No response to onStopJob'.

שגיאת ה-ANR הזאת יכולה לנבוע מ-2 תרחישים: 1. יש עבודה שחוסמת את ה-thread הראשי, ומונעת את ההפעלה וההשלמה של פונקציות ה-callbacks onStartJob או onStopJob במסגרת מגבלת הזמן הצפויה. 2. המפתח מפעיל עבודה חוסמת בתוך פונקציית ה-callback onStartJob או onStopJob של JobScheduler, וכתוצאה מכך פונקציית ה-callback לא מסתיימת במסגרת מגבלת הזמן הצפויה.

כדי לטפל בבעיה מס' 1, צריך לנפות באגים ולבדוק מה חוסם את ה-thread הראשי כשמתרחש ה-ANR. אפשר לעשות זאת באמצעות ApplicationExitInfo#getTraceInputStream() כדי לקבל את הטראס של tombstone כשמתרחש ה-ANR. אם מצליחים לשחזר את שגיאת ה-ANR באופן ידני, אפשר לתעד מעקב אחר המערכת ולבדוק את המעקב באמצעות Android Studio או Perfetto כדי להבין טוב יותר מה פועל ב-thread הראשי כשמתרחשת ה-ANR. חשוב לזכור שזה יכול לקרות כשמשתמשים ב-JobScheduler API ישירות או באמצעות ספריית androidx WorkManager.

כדי לטפל בבעיה השנייה, כדאי לעבור ל-WorkManager, שמספק תמיכה באריזת כל עיבוד ב-onStartJob או ב-onStopJob בשרשור אסינכרוני.

JobScheduler גם כולל דרישה להצהיר על הרשאת ACCESS_NETWORK_STATE אם משתמשים ב-setRequiredNetworkType או אילוץ של setRequiredNetwork. אם האפליקציה לא מצהירה על ההרשאה ACCESS_NETWORK_STATE כשמגדירים את התזמון של המשימה, והיא מטרגטת את Android מגרסה 14 ואילך, תופיע הודעת השגיאה SecurityException.

Tiles launch API

对于以 Android 14 及更高版本为目标平台的应用, TileService#startActivityAndCollapse(Intent) 已弃用,现在会抛出 调用时抛出异常。如果您的应用从功能块启动 activity,请使用 TileService#startActivityAndCollapse(PendingIntent)

פרטיות

גישה חלקית לתמונות ולסרטונים

Android 14 introduces Selected Photos Access, which allows users to grant apps access to specific images and videos in their library, rather than granting access to all media of a given type.

This change is only enabled if your app targets Android 14 (API level 34) or higher. If you don't use the photo picker yet, we recommend implementing it in your app to provide a consistent experience for selecting images and videos that also enhances user privacy without having to request any storage permissions.

If you maintain your own gallery picker using storage permissions and need to maintain full control over your implementation, adapt your implementation to use the new READ_MEDIA_VISUAL_USER_SELECTED permission. If your app doesn't use the new permission, the system runs your app in a compatibility mode.

חוויית משתמש

התראות מאובטחות לגבי Intent במסך מלא

With Android 11 (API level 30), it was possible for any app to use Notification.Builder.setFullScreenIntent to send full-screen intents while the phone is locked. You could auto-grant this on app install by declaring USE_FULL_SCREEN_INTENT permission in the AndroidManifest.

Full-screen intent notifications are designed for extremely high-priority notifications demanding the user's immediate attention, such as an incoming phone call or alarm clock settings configured by the user. For apps targeting Android 14 (API level 34) or higher, apps that are allowed to use this permission are limited to those that provide calling and alarms only. The Google Play Store revokes default USE_FULL_SCREEN_INTENT permissions for any apps that don't fit this profile. The deadline for these policy changes is May 31, 2024.

This permission remains enabled for apps installed on the phone before the user updates to Android 14. Users can turn this permission on and off.

You can use the new API NotificationManager.canUseFullScreenIntent to check if your app has the permission; if not, your app can use the new intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT to launch the settings page where users can grant the permission.

אבטחה

הגבלות על אובייקטים מרומזים של Intent ועל אובייקטים של Intent בהמתנה

באפליקציות שמטרגטות ל-Android 14 (רמת API ‏34) ואילך, מערכת Android מגבילה את היכולת של האפליקציות לשלוח כוונות משתמשים מרומזות לרכיבים פנימיים של האפליקציה בדרכים הבאות:

  • כוונות משתמשים מרומזות מועברות רק לרכיבים שיוצאו. אפליקציות צריכות להשתמש ב-Intent מפורש כדי לשלוח נתונים לרכיבים שלא יוצאו, או לסמן את הרכיב כמיוצא.
  • אם אפליקציה יוצרת PendingIntent שניתנים לשינוי עם כוונה שלא מציינת רכיב או חבילה, המערכת מקפיצה הודעת שגיאה (throw) לחריגה.

השינויים האלה מונעים מאפליקציות זדוניות ליירט כוונות מרומזות שנועדו לשימוש ברכיבים הפנימיים של האפליקציה.

לדוגמה, לפניכם מסנן כוונת רכישה שאפשר להצהיר בקובץ המניפסט של האפליקציה:

<activity
    android:name=".AppActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.action.APP_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

אם האפליקציה ניסתה להפעיל את הפעילות הזו מתוך כוונה מרומזת, המערכת תבטל את החריגה ActivityNotFoundException:

Kotlin

// Throws an ActivityNotFoundException exception when targeting Android 14.
context.startActivity(Intent("com.example.action.APP_ACTION"))

Java

// Throws an ActivityNotFoundException exception when targeting Android 14.
context.startActivity(new Intent("com.example.action.APP_ACTION"));

כדי להפעיל את הפעילות שלא יוצאה, האפליקציה צריכה להשתמש ב-Intent מפורש:

Kotlin

// This makes the intent explicit.
val explicitIntent =
        Intent("com.example.action.APP_ACTION")
explicitIntent.apply {
    package = context.packageName
}
context.startActivity(explicitIntent)

Java

// This makes the intent explicit.
Intent explicitIntent =
        new Intent("com.example.action.APP_ACTION")
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);

מקלטי שידורים שרשומים בזמן ריצה חייבים לציין את התנהגות הייצוא

Apps and services that target Android 14 (API level 34) or higher and use context-registered receivers are required to specify a flag to indicate whether or not the receiver should be exported to all other apps on the device: either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED, respectively. This requirement helps protect apps from security vulnerabilities by leveraging the features for these receivers introduced in Android 13.

Exception for receivers that receive only system broadcasts

If your app is registering a receiver only for system broadcasts through Context#registerReceiver methods, such as Context#registerReceiver(), then it shouldn't specify a flag when registering the receiver.

טעינה בטוחה יותר של קוד דינמי

如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台并使用动态代码 正在加载 (DCL),所有动态加载的文件都必须标记为只读。 否则,系统会抛出异常。我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而遭到入侵的风险。

如果必须动态加载代码,请使用以下方法,在动态文件(例如 DEX、JAR 或 APK 文件)打开并写入任何内容之前立即将其设为只读:

Kotlin

val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly()
    // Then write the actual file content
}
val cl = PathClassLoader(jar, parentClassLoader)

Java

File jar = new File("DYNAMICALLY_LOADED_FILE.jar");
try (FileOutputStream os = new FileOutputStream(jar)) {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly();
    // Then write the actual file content
} catch (IOException e) { ... }
PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);

处理已存在的动态加载文件

为防止系统对现有动态加载的文件抛出异常,我们建议您先删除并重新创建文件,然后再尝试在应用中重新动态加载这些文件。重新创建文件时,请按照上述指南在写入时将文件标记为只读。或者,您可以将现有文件重新标记为只读,但在这种情况下,我们强烈建议您先验证文件的完整性(例如,对照可信值检查文件的签名)以保护应用免遭恶意操作的影响。

הגבלות נוספות על התחלת פעילויות מהרקע

באפליקציות שמטרגטות ל-Android 14 (רמת API 34) ומעלה, המערכת מגבילה עוד יותר את הזמנים שבהם אפליקציות יכולות להתחיל פעילויות מהרקע:

  • כשאפליקציה שולחת PendingIntent באמצעות PendingIntent#send() או שיטות דומות, היא צריכה להביע הסכמה אם היא רוצה להעניק לעצמה הרשאות להפעלת פעילות ברקע כדי להפעיל את ה-Intent בהמתנה. כדי להביע הסכמה, האפליקציה צריכה להעביר חבילת ActivityOptions עם setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED).
  • כשאפליקציה גלויה מקשרת שירות של אפליקציה אחרת שנמצאת ברקע באמצעות השיטה bindService(), האפליקציה הגלויה צריכה להביע הסכמה אם היא רוצה להעניק לשירות המקושר את ההרשאות שלה להפעלת פעילות ברקע. כדי להביע הסכמה, האפליקציה צריכה לכלול את הדגל BIND_ALLOW_ACTIVITY_STARTS בקריאה לשיטה bindService().

השינויים האלה מרחיבים את קבוצת ההגבלות הקיימת כדי להגן על המשתמשים. הם מונעים מאפליקציות זדוניות לנצל לרעה ממשקי API כדי להפעיל פעילויות מפריעות מהרקע.

פרצת אבטחה מסוג Path Traversal בקובץ ZIP

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式防止 Zip 路径遍历漏洞:如果 Zip 文件条目名称包含“..”或以“/”开头,ZipFile(String)ZipInputStream.getNextEntry() 会抛出 ZipException

应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback() 选择停用此验证。

באפליקציות שמיועדות ל-Android 14 (רמת API ‏34) ואילך, MediaProjection#createVirtualDisplay יוצרת את השגיאה SecurityException באחת מהתרחישים הבאים:

האפליקציה שלכם צריכה לבקש מהמשתמש להביע הסכמה לפני כל סשן צילום. סשן צילום יחיד הוא קריאה יחידה ל-MediaProjection#createVirtualDisplay, וצריך להשתמש בכל מכונה של MediaProjection רק פעם אחת.

טיפול בשינויים בתצורה

אם האפליקציה צריכה להפעיל את MediaProjection#createVirtualDisplay כדי לטפל בשינויים בתצורה (כמו שינוי כיוון המסך או גודל המסך), תוכלו לפעול לפי השלבים הבאים כדי לעדכן את VirtualDisplay למכונה הקיימת של MediaProjection:

  1. קוראים ל-VirtualDisplay#resize עם הרוחב והגובה החדשים.
  2. מספקים Surface חדש עם הרוחב והגובה החדשים ל-VirtualDisplay#setSurface.

רישום של קריאה חוזרת

האפליקציה צריכה לרשום קריאה חוזרת (callback) כדי לטפל במקרים שבהם המשתמש לא נותן הסכמה להמשך סשן הצילום. כדי לעשות זאת, מטמיעים את Callback#onStop ומאפשרים לאפליקציה לשחרר את המשאבים הקשורים (כמו VirtualDisplay ו-Surface).

אם האפליקציה לא תירשם את קריאת החזרה (callback) הזו, MediaProjection#createVirtualDisplay תשליך IllegalStateException כשהאפליקציה תפעיל אותה.

הגבלות מעודכנות שאינן ב-SDK

‫Android 14 כולל רשימות מעודכנות של ממשקי non-SDK מוגבלים, שמבוססות על שיתוף פעולה עם מפתחי Android ועל הבדיקות הפנימיות האחרונות. כשאפשר, אנחנו מוודאים שיש חלופות ציבוריות לפני שאנחנו מגבילים ממשקים שאינם ב-SDK.

אם האפליקציה שלכם לא מטרגטת ל-Android 14, יכול להיות שחלק מהשינויים האלה לא ישפיעו עליכם באופן מיידי. עם זאת, למרות שכרגע אפשר להשתמש בממשקים מסוימים שאינם SDK (בהתאם לרמת ה-API לטירגוט של האפליקציה), שימוש בשיטה או בשדה כלשהם שאינם SDK תמיד כרוך בסיכון גבוה להפסקת הפעולה של האפליקציה.

אם אתם לא בטוחים אם האפליקציה שלכם משתמשת בממשקים שאינם SDK, אתם יכולים לבצע בדיקה לאפליקציה כדי לגלות זאת. אם האפליקציה שלכם מסתמכת על ממשקים שלא נכללים ב-SDK, כדאי להתחיל לתכנן מעבר לחלופות ל-SDK. עם זאת, ברור לנו שיש אפליקציות שבהן יש תרחישי שימוש לגיטימיים בממשקים שאינם SDK. אם אין לכם אפשרות להשתמש בממשק חלופי במקום בממשק שאינו ב-SDK עבור תכונה באפליקציה, עליכם לבקש ממשק API ציבורי חדש.

如需详细了解此 Android 版本中的变更,请参阅 Android 14 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制