בדומה לגרסאות קודמות, Android 14 כולל שינויים בהתנהגות שעשויים להשפיע על האפליקציה שלכם. שינויי ההתנהגות הבאים חלים אך ורק על אפליקציות שמטרגטות ל-Android 14 (רמת API 34) ואילך. אם האפליקציה שלכם מטרגטת את Android 14 ואילך, עליכם לשנות אותה כך שתתמוך בהתנהגויות האלה בצורה תקינה, במקרים הרלוונטיים.
חשוב גם לבדוק את רשימת שינויי ההתנהגות שמשפיעים על כל האפליקציות שפועלות ב-Android 14, ללא קשר ל-targetSdkVersion
של האפליקציה.
פונקציונליות עיקרית
חובה לציין סוגי שירותים שפועלים בחזית
אם האפליקציה שלכם מטרגטת ל-Android 14 (רמת API 34) ואילך, צריך לציין לפחות סוג אחד של שירות שפועל בחזית לכל שירות שפועל בחזית באפליקציה. כדאי לבחור סוג של שירות שפועל בחזית שמייצג את תרחיש השימוש של האפליקציה. המערכת מצפה ששירותים שפועלים בחזית עם סוג מסוים יעמדו בדרישות של תרחיש לדוגמה מסוים.
אם תרחיש שימוש באפליקציה לא משויך לאף אחד מהסוגים האלה, מומלץ מאוד להעביר את הלוגיקה שלכם כך שתשתמש ב-WorkManager או במשימות להעברת נתונים שמבוצעות ביוזמת המשתמש.
אכיפה של ההרשאה BLUETOOTH_CONNECT ב-BluetoothAdapter
Android 14 enforces the BLUETOOTH_CONNECT
permission when calling the
BluetoothAdapter
getProfileConnectionState()
method for apps targeting
Android 14 (API level 34) or higher.
This method already required the BLUETOOTH_CONNECT
permission, but it was not
enforced. Make sure your app declares BLUETOOTH_CONNECT
in your app's
AndroidManifest.xml
file as shown in the following snippet and check that
a user has granted the permission before calling
getProfileConnectionState
.
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
עדכונים ל-OpenJDK 17
ב-Android 14 אנחנו ממשיכים לעדכן את ספריות הליבה של Android כדי להתאים אותן לתכונות בגרסאות OpenJDK LTS האחרונות, כולל עדכוני ספריות ותמיכה בשפה Java 17 למפתחי אפליקציות ופלטפורמות.
חלק מהשינויים האלה עשויים להשפיע על תאימות האפליקציות:
- שינויים בביטויים רגולריים: אסור עכשיו להשתמש בהפניות לא חוקיות לקבוצות, כדי להתאים את הקוד יותר לסמנטיקה של OpenJDK. יכול להיות שתראו מקרים חדשים שבהם המערכת תזרוק
IllegalArgumentException
מהקלאסjava.util.regex.Matcher
, לכן חשוב לבדוק את האפליקציה לאזורים שבהם נעשה שימוש בביטויים רגולריים. כדי להפעיל או להשבית את השינוי הזה במהלך הבדיקה, משנים את מצב הדגלDISALLOW_INVALID_GROUP_REFERENCE
באמצעות כלים של מסגרת התאימות. - טיפול ב-UUID: השיטה
java.util.UUID.fromString()
מבצעת עכשיו בדיקות מחמירות יותר במהלך אימות הארגומנט של הקלט, כך שיכול להיות שתראוIllegalArgumentException
במהלך ביטול הסריאליזציה. כדי להפעיל או להשבית את השינוי הזה במהלך הבדיקה, משנים את מצב הדגלENABLE_STRICT_VALIDATION
באמצעות כלים של מסגרת התאימות. - בעיות ב-ProGuard: במקרים מסוימים, הוספת הכיתה
java.lang.ClassValue
גורמת לבעיה אם מנסים לכווץ, להסתיר ולבצע אופטימיזציה של האפליקציה באמצעות ProGuard. הבעיה נובעת מספריית Kotlin שמשנה את התנהגות זמן הריצה בהתאם לכך ש-Class.forName("java.lang.ClassValue")
מחזירה סוג או לא. אם האפליקציה שלכם פותחה בגרסה ישנה יותר של סביבת זמן הריצה, בלי הכיתהjava.lang.ClassValue
, יכול להיות שהאופטימיזציות האלה יגרמו להסרת השיטהcomputeValue
מכיתות שמקורן ב-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)
הוצא משימוש ועכשיו הוא גורם להשלכת חריגה כשמנסים להפעיל אותו. אם האפליקציה מפעילה פעילויות מכרטיסי מידע, משתמשים
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.
אבטחה
הגבלות על כוונות מרומזות ועל כוונות בהמתנה
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式限制应用向内部应用组件发送隐式 intent:
- 隐式 intent 只能传送到导出的组件。应用必须使用显式 intent 传送到未导出的组件,或将该组件标记为已导出。
- 如果应用通过未指定组件或软件包的 intent 创建可变待处理 intent,系统会抛出异常。
这些变更可防止恶意应用拦截意在供应用内部组件使用的隐式 intent。
例如,下面是可以在应用的清单文件中声明的 intent 过滤器:
<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>
如果应用尝试使用隐式 intent 启动此 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"));
如需启动非导出的 activity,应用应改用显式 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);
מקלטי שידורים שנרשמו בזמן ריצה חייבים לציין את התנהגות הייצוא
以 Android 14(API 级别 34)或更高版本为目标平台并使用上下文注册的接收器的应用和服务必须指定以下标志,以指明接收器是否应导出到设备上的所有其他应用:RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。此要求有助于利用 Android 13 中引入的这些接收器的功能,来保护应用免受安全漏洞的影响。
仅接收系统广播的接收器的例外情况
如果您的应用仅通过 Context#registerReceiver
方法(例如 Context#registerReceiver()
)针对系统广播注册接收器,那么它在注册接收器时不应指定标志。
טעינה בטוחה יותר של קוד דינמי
אם האפליקציה שלכם מטרגטת את 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)或更高版本为目标平台的应用,系统会进一步限制允许应用在后台启动 activity 的时间:
- 现在,当应用使用
PendingIntent#send()
或类似方法发送PendingIntent
时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。如需选择启用,应用应通过setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
传递ActivityOptions
软件包。 - 当可见应用使用
bindService()
方法绑定其他在后台应用的服务时,如果可见应用想要授予自己的后台 activity 对绑定服务的启动特权,则必须选择启用。如需选择启用,应用应在调用bindService()
方法时包含BIND_ALLOW_ACTIVITY_STARTS
标志。
这些更改扩大了一组现有限制条件的范围,目的是防止恶意应用滥用 API 以在后台启动干扰性活动,从而保护用户。
התקפת Path Traversal בקובץ ZIP
באפליקציות שמיועדות ל-Android 14 (רמת יעד API 34) ואילך, Android מונע את נקודת החולשה של מעבר נתיב בקובצי ZIP באופן הבא: ZipFile(String)
ו-ZipInputStream.getNextEntry()
גורמים להצגת השגיאה ZipException
אם שמות הרשומות בקובץ ה-ZIP מכילים את הסימן '..' או מתחילים בסימן '/'.
אפליקציות יכולות לבטל את האימות הזה על ידי קריאה ל-dalvik.system.ZipPathValidator.clearCallback()
.
נדרשת הסכמת המשתמש בכל סשן הקלטה של MediaProjection
For apps targeting Android 14 (API level 34) or higher, a SecurityException
is
thrown by MediaProjection#createVirtualDisplay
in either of the following
scenarios:
- Your app caches the
Intent
that is returned fromMediaProjectionManager#createScreenCaptureIntent
, and passes it multiple times toMediaProjectionManager#getMediaProjection
. - Your app invokes
MediaProjection#createVirtualDisplay
multiple times on the sameMediaProjection
instance.
Your app must ask the user to give consent before each capture session. A single
capture session is a single invocation on
MediaProjection#createVirtualDisplay
, and each MediaProjection
instance must
be used only once.
Handle configuration changes
If your app needs to invoke MediaProjection#createVirtualDisplay
to handle
configuration changes (such as the screen orientation or screen size changing),
you can follow these steps to update the VirtualDisplay
for the existing
MediaProjection
instance:
- Invoke
VirtualDisplay#resize
with the new width and height. - Provide a new
Surface
with the new width and height toVirtualDisplay#setSurface
.
Register a callback
Your app should register a callback to handle cases where the user doesn't grant
consent to continue a capture session. To do this, implement
Callback#onStop
and have your app release any related resources (such as
the VirtualDisplay
and Surface
).
If your app doesn't register this callback,
MediaProjection#createVirtualDisplay
throws an IllegalStateException
when your app invokes it.
עדכון ההגבלות על רכיבים שאינם SDK
Android 14 includes updated lists of restricted non-SDK interfaces based on collaboration with Android developers and the latest internal testing. Whenever possible, we make sure that public alternatives are available before we restrict non-SDK interfaces.
If your app does not target Android 14, some of these changes might not immediately affect you. However, while you can currently use some non-SDK interfaces (depending on your app's target API level), using any non-SDK method or field always carries a high risk of breaking your app.
If you are unsure if your app uses non-SDK interfaces, you can test your app to find out. If your app relies on non-SDK interfaces, you should begin planning a migration to SDK alternatives. Nevertheless, we understand that some apps have valid use cases for using non-SDK interfaces. If you cannot find an alternative to using a non-SDK interface for a feature in your app, you should request a new public API.
מידע נוסף על השינויים בגרסה הזו של Android זמין במאמר עדכונים להגבלות על ממשקים שאינם SDK ב-Android 14. מידע נוסף על ממשקים שאינם ב-SDK זמין במאמר הגבלות על ממשקים שאינם ב-SDK.