What’s New in Android™ 14 and the Impact on Zebra developers
Hello Developers, it's that time of year again – the days are getting shorter, the nights are filled with coding, and Android 14 is making its way to Zebra devices! And while that might spark a flicker of "Oh no, what do I have to change now?" in the hearts of developers, take a deep breath. For the vast majority of apps, the transition will be smooth sailing.
However, some relevant shifts and new features under the hood might need your attention. We're not talking about a ground-up rewrite, and in many cases, your apps will work flawlessly with zero changes. And even when adjustments are needed, they're often minor tweaks rather than monumental overhauls.
This blog post is your guide to navigating the Android 14 landscape. I'll explore the key changes, highlight potential compatibility hiccups, and equip you with the knowledge to make the transition as seamless as possible. So grab your favorite beverage, settle in, and let's explore what Android 14 has in store for us!
Here is the list of topics I'm going to dive into.
SECURITY FEATURES AFFECTING ALL APPS
SECURITY FEATURES AFFECTING APPS TARGETING API LEVEL 34+
- Credential Manager
- Screenshot detection
- Restrictions to implicit and pending intents
- Restrictions on background work
PERFORMANCE AND CORE FEATURES AFFECTING ALL APPS
PERFORMANCE AND CORE FEATURES AFFECTING APPS TARGETING API LEVEL 34+
ANDROID 14 FOR ENTERPRISE
Notes to the reader.
- All blog code was developed and tested on Zebra AT_FULL_UPDATE_14-19-22.00-UG-U00-PRD-ATH-04 BSP.
- Reference code is found here https://github.com/ZebraDevs/A14-CHALLENGER
- Text version 36
- "Android™" is a trademark of Google LLC.
Minimum Installable Target API (and view on Android’s Compatibility Framework Tools)
Level One of the significant changes in Android™14 is the introduction of a minimum installable target API level (23). This means that apps must target a certain API level to be installable on devices running Android 14, currently that is API level 23 (Android 6 Marshmallow). This change aims to improve the security and performance of apps, ensuring they use modern practices and APIs. By enforcing a minimum API level, Google aims to reduce the number of legacy apps that may not adhere to the latest security protocols and performance standards. There are also security reasons behind this rule. By enforcing a higher minimum target API level, Google ensures that apps leverage the latest improvements, reducing the risk of vulnerabilities and exploits. Finally, this helps minimize ecosystem fragmentation. Read Google’s documentation here: https://developer.android.com/about/versions/14/behavior-changes-all#minimum-target-api-level
And here is what I found when I challenged this new requirement.
-
Manually installing the app via adb
-
or tapping on the APK file
persistently fails.
However, the task can be achieved using the debug tools (either through Android Studio or adb).
The disclaimer below is displayed two times after installation and every time after the app’s storage is depleted.
Furthermore, if Play Protect is enabled, you might also see this prompt (documented here: https://developers.google.com/android/play-protect/warning-dev-guidance#android_app_compatibility_too_low_warning)
Implications for Developers and Best Practices—Compatibility
Developers need to ensure their apps are compatible with Android 14. This may involve updating existing code, using compatibility libraries, or providing alternative implementations for older Android versions. Proactively test your old app leveraging Android’s Compatibility Framework Tools introduced here https://developer.android.com/guide/app-compatibility/test-debug# and also here specifically for Android 14: https://developer.android.com/about/versions/14/reference/compat-framework-changes .
Furthermore, when you build an Android app, you have three settings that determine how it interacts with different Android versions:
- compileSdk - This is like choosing your toolbox. It determines which Android version's tools and APIs you can use to build your app. A higher compileSdk gives you access to newer features and functionalities.
- targetSdk - This is like setting the stage for your app's performance. It tells the system which Android version your app is designed for. The system might adjust its behavior to ensure your app runs smoothly on that version.
- minSdk - It specifies the minimum Android version required to install and run your app. Devices with older versions won't be able to run it.
You can update these settings independently. For example, you can use a newer compileSdk to access the latest tools while keeping your targetSdk the same to avoid potential compatibility issues.
Overall, the compatibility framework offers several advantages. It allows to:
- Test targeted changes without actually changing the app's targetSdkVersion. You can use the toggles to force-enable specific targeted behavior changes to evaluate the impact on your existing app.
- Focus your testing on specific changes only. Rather than addressing all targeted changes simultaneously, the toggles let you turn off all targeted changes except the ones you want to test against.
- Manage toggles through adb. You can use adb commands to turn the toggleable changes in your automated test environment on and off.
- Debug faster using standard change IDs. Toggleable changes each have a unique ID and name that you can use to debug root causes in the log output quickly.
For instance, let’s exercise the permission type for FGS in Android 14. When targeting API 34+, this feature requires that the app Manifest holds a specific permission type.
Please do the following to see how your app will be good when you upgrade it to target API level 34.
-
start running your app (which targets, e.g., API 29) as it is on Android 14, and look at the Logcat. Everything runs smoothly, even if the FSG type has no specific permission declared
-
Then enable the specific feature through the Developer Options / App Compatibility Changes toggle “FGS_TYPE_PERMISSION_CHANGE_ID.”
Again, you can just run the app and look at the Logcat. You’ll get an exception!
To clear the exception, explicitly grant the Manifest permission FOREGROUND_SERVICE_MEDIA_PLAYBACK.
Try it, and your app will be back on track! This tells you that when you upgrade your app to target API34, you’ll need to touch the Manifest.xml in the permissions section.
Key Takeaway—To combat malware that often targets older Android versions, Android 14 raises the minimum installable target API level: users are prevented from installing apps targeting API levels below 23 according to the standard workflow. However, proper workarounds are provided for developing purposes to allow installation and testing. Also, on devices upgrading to Android 14, apps with a targetSdkVersion lower than 23 will remain installed. This move encourages developers to keep their apps up-to-date with the latest security and privacy practices, safeguarding users from potential threats. This change is a significant step towards improving the overall Android ecosystem for users and developers.
Credential Manager
Android 14 introduces a revamped Credential Manager, providing a more secure and user-friendly way for apps to handle user credentials. This unified system simplifies authentication flows and improves security by storing credentials in a protected environment. The Credential Manager can now handle different types of credentials, such as passwords and federated login tokens, by providing a consistent interface for developers. The Credential Manager aims to improve security by encouraging more robust, more secure authentication methods and reducing the reliance on passwords. Please remember that Credential Manager is part of Play Services.
It has been designed to support back Android 4.4 (API level 19) devices through a Jetpack Library using Google Play services.
Credential Manager offers several services to developers:
- Traditional username and password
- Passkeys - which is based on the Fido Alliance industry standard https://fidoalliance.org/how-fido-works/ and https://developer.android.com/identity/sign-in/credential-manager#about-passkeys
- Federated sign-in solutions
- Enhanced Autofill
USERNAME AND PASSWORDS—The Credential Manager makes it easier to manage usernames and passwords, and I'm sharing the following lines of code so you can learn how it works. First, could you import the correct libs into your project?
At the time of writing, Credential Manager v1.2.2 was the latest stable version available.
So, in Kotlin gradle, ensure to include these two libraries.
And if in doubt when prompted about what version to import, choose the one with the @1.2.2 inside
Add this code when you need to save new credentials.
Such code is also available on Github
The key actions are
- Create an instance of the CredentialManager
- Submit a password creation request by calling the createCredential method
- Manage exceptions, e.g., the user canceling the request or if it fails
Be mindful that the createCredential
function is of suspend type, so it needs to be called from inside a coroutine scope.
And here is what you'll get
Should the user reject the Save action by pressing 'Never,' an exception is returned.
Exception handling com.ndzl.a14challenger D CreateCredentialException During save password, found password failure response from one tap 16: [28435] The save prompt is disabled for the current app. To restore, remove this app from the "Never save" list in the Smart Lock for Passwords settings for all accounts on this device.
So, to remove the exception just added to the Password Manager, you need to visit
Settings / Passwords and Accounts / Autofill service / Google Password Manager / Settings icon:
And what if no Google Account is set on the device? Sadly, you get this error message.
“CreateCredentialException During save password, found password failure response from one tap 16: [28434] Cannot find an eligible account.”
Finally, to retrieve a saved password, use this code
-
The Credential Manager instance must already exist or needs to be created as in the save case
-
Then define the GetCredentialOptions
-
Eventually, call the
credentialManager.getCredential
- It returns an object structured like this
get result android.credentials.TYPE_PASSWORD_CREDENTIAL Bundle[{androidx.credentials.BUNDLE_KEY_ID=ndzl, androidx.credentials.BUNDLE_KEY_PASSWORD=Zebra123}]
PASSKEYS - Passkeys are probably the most exciting feature in the new Credential Manager and are extensively covered in Google's blog post https://android-developers.googleblog.com/2023/07/credential-manager-beta-easy-secure-authentication-with-passkeys-on-android.html
Passkeys are next-generation credential standards designed to replace traditional passwords. Passkeys leverage strong cryptographic techniques and biometric authentication to provide a more secure and user-friendly login experience. Credential Manager seamlessly integrates with passkeys, allowing users to create, store, and use passkeys across different apps and websites.
Passkeys are a safer and easier replacement for passwords. With passkeys, users can sign in to apps and websites using a biometric sensor (such as a fingerprint or facial recognition), PIN, or pattern. This provides a seamless sign-in experience, freeing your users from having to remember usernames or passwords.
Passkeys rely on WebAuthn (Web Authentication), a standard jointly developed by the FIDO Alliance and the World Wide Web Consortium (W3C). WebAuthn uses public-key cryptography to authenticate the user. The website or app the user is signing into can see and store the public key but never the private one. The private key is kept secret and safe. And because the key is unique and tied to the website or app, passkeys are un-phishable, adding further security. Credential Manager allows users to create passkeys and store them in Google Password Manager.
FEDERATED SIGN-IN - To start working with Federated Sign-in, I found this helpful https://medium.com/androidbytesensei/integrate-credential-manager-with-signin-with-google-5680762cec06
Here, I did some experimenting with it.
- The sample code to refer to is https://github.com/NDZL/A14-CHALLENGER/blob/6a2fb9a2287001b397f3b9d86b7b2a60ef6b0d48/app/src/main/java/com/ndzl/a14challenger/MainActivity2.kt#L133
- A key point about this method is to get a
setServerClientId
, which sits in the Google Cloud Console- For this, you need to set up a cloud project and the related Credentials, specifically linked to your Android app and its signing certificate.
-
If you are using Android's debug certificate, get the SHA1 signature with this command from the project console
./gradlew signingReport
-
for a production keystore, use
keytool -keystore <path-to-debug-or-production-keystore> -list -v
-
with that information, Google Cloud creates a ClientID for Android, and that's the string you need to paste in the Kotlin code
-
the same credentials must be used to log in as a Google Account on the Android device
With the proper setup in place, your sign-in workflow starts with this prompt
AUTOFILL - The autofill experience in Android 14 receives notable improvements, making it more intelligent and context-aware. Credential Manager can better identify login fields and suggest relevant credentials based on the current app or website. This streamlines the login process and reduces users' need to enter their credentials manually.
Credential Manager in Android 14 marks a significant step towards a passwordless future, offering users a more secure, convenient, and user-friendly way to manage their credentials. Developers are encouraged to leverage these new features to enhance their apps and provide a seamless login experience for their users.
Screenshot Detection
In Android 14, Google introduced a significant change in how apps can detect screenshots being taken on the device. Previously, apps could broadly monitor for screenshots using the ACTION_USER_PRESENT broadcast. However, in Android 14, this capability is restricted to enhance user privacy and security. This change aims to prevent apps from indiscriminately tracking user activity and potentially compromising sensitive information captured in screenshots. This privacy-preserving API invokes a callback and displays a toast message when the user takes a screenshot while an app activity is visible. The backgrounds are explained here
Specific Use Cases Allowed-While general screenshot detection is restricted, Android 14 provides a new API, onScreenshotTaken()
, within the Activity class, allowing apps to detect screenshots only within their own app's bounds. This means an app can be notified when a screenshot of its content is explicitly taken. By limiting screenshot detection to an app's content, Android 14 also reduces the risk of malicious apps monitoring screenshots containing sensitive data from other apps.
It's worth noting that Zebra provided Administrators with a way to prevent screenshots starting with Mx5.0. See Techdocs/#screen-shot-enabledisable if you need to extend the Android 14 feature about screenshot limitation to previous platforms.
Examples
-
My app detects a screenshot taken manually by pressing the power button and the volume down
To achieve that, I implemented the
registerScreenCaptureCallback(mainExecutor, screenCaptureCallback)
based onval screenCaptureCallback = Activity.ScreenCaptureCallback
.Note that the toast message is managed directly by the OS. Find the source code about this feature here
-
I also tried capturing the screen content via Media Projection (the sample app's button 'TAKE SCREENSHOT'). In that case, the app is not notified of the screens taken.
See the code using Media Projection here
The changes to screenshot detection in Android 14 prioritize user privacy and security by restricting general screenshot monitoring. Developers must adapt their strategies for handling sensitive content and leverage the new onScreenshotTaken() API for specific use cases within their app's bounds. Finally, to prevent taking screenshots at all, use this command
activity.getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE)
Restrictions to implicit and pending intents
Android 14 places additional restrictions on implicit and pending intents. Implicit intents, which do not specify a particular component, can now only be sent to components that are explicitly declared to handle them. This change improves security by ensuring that intents are not inadvertently sent to unintended recipients. Pending intents, which allow future operations to be executed on behalf of an app, also face tighter restrictions to prevent misuse and potential security vulnerabilities. So, these restrictions limit how apps can interact with each other and access sensitive information through intents.
Key change details are listed below.
Restricted Implicit Intents - Android 14 imposes stricter limitations on using implicit intents, particularly for actions that could expose sensitive data or functionalities. Apps targeting Android 14 need to be more explicit in defining the target components for their intents, reducing the risk of unintended data exposure or unauthorized access. There's already a good example on the Android documentation page where you can see this in action.
Mutability Restrictions for Pending Intents - Pending intents, which are used to defer intent resolution until a later time, are now subject to mutability restrictions in Android 14. This means that certain modifications to pending intents, such as changing the target component or adding sensitive data, may be restricted to prevent potential security vulnerabilities.
Reasons for the Change: to mitigate security risks associated with unintended data sharing, unauthorized access to app components, and potential exploitation of intent vulnerabilities. Also, by limiting how apps can interact with each other through intents, Android 14 strengthens user privacy protections and reduces the risk of sensitive information being accessed or shared without explicit user consent.
Implications for Developers - Developers must adopt a more explicit approach when using intents, clearly defining the target components and minimizing using implicit intents for sensitive actions.
Pending Intent Management: Carefully manage the mutability of pending intents, ensuring that any modifications adhere to the new restrictions and do not introduce security vulnerabilities. Look at this code fragment to see how I managed to mitigate the newly introduced change in API level 34+. A setClassName
solved the issue.
Key Takeaway: The restrictions on implicit and pending intents in Android 14 are crucial steps towards a more secure and privacy-focused Android ecosystem. Developers need to adapt their intent usage patterns and adopt more explicit and secure communication mechanisms to ensure their apps comply with the new guidelines and protect user data.
Restrictions on background work
This section is meant to cover a bunch of changes:
- "Background Work Disallowed Shortly After Entering Cached State" (all apps running on A14)
- "Apps Can Kill Only Their Own Background Processes" (all apps running on A14)
- "Additional Restrictions on Starting Activities from the Background" (for apps targeting API34+)
CACHED STATES
In Android 14, background work is disallowed shortly after an app process enters a cached state. This change is designed to optimize battery life and performance further. When an app is in a cached state, the user is not actively interacting with it. Therefore, restricting background activities for such apps ensures that system resources are allocated to apps and services that the user is actively using.
Such behavior matches another change, about the fact that the System enforces cached-app resource usage: by design, an app's process is in a cached state when it's moved to the background and no other app process components are running.
Developers are advised to use WorkManager or JobScheduler to schedule background tasks with appropriate constraints and delays. They should also implement robust state management mechanisms to ensure that background tasks can be resumed or rescheduled appropriately when the app transitions out of the cached state. This is because - as stated here - Apps that use typical framework-supported lifecycle APIs – such as services, JobScheduler, and Jetpack WorkManager – shouldn't be impacted by these changes.
Let's put this to work!
I've set up a few ways to perform work in the background in this routine
- a Runnable object
- a Coroutine
- a Java thread
- a JobScheduler
- a WorkManager
The cached state started when the app was run in the background and the onStop()
method was invoked.
In a run, the first three workers stopped between 2 and 3 minutes after the app entered the cached state. Such time, however, is unpredictable, so it's unrepeatable.
Here is another run when even a Java thread-based counter was added, lasting around 10 minutes.
So, don't rely on such low-hanging solutions since they might not work as expected!
As seen above, Google recommends using the standard lifecycle tools to manage work in the background. Unfortunately, JobSchedulers and WorkManagers are subject to a minimum periodic interval of 15 minutes.
If you try to make it faster, your value will be authored: Interval duration lesser than minimum allowed value; Changed to 900000
.
Please take it as a clear message that a fast periodic job must be done in the foreground and that the background is the right place for slowly periodic tasks.
And yes! I could also see the WorkManager doing its work after 15 minutes!
The JobScheduler also did its duty; it was called around every 12 minutes during my tests.
KILLING OWN PROCESSES
To enhance security and system stability, Android 14 restricts apps to only killing their background processes. In previous versions, apps could kill processes of other apps, which could lead to instability and security risks. This change ensures that apps are more sandboxed and that no app can interfere with the operations of another, thereby improving overall system reliability. This means an app can no longer terminate processes belonging to other apps, even if it previously had the necessary permissions. The killBackgroundProcesses
API usage is given here.
This restriction prevents malicious apps from forcefully terminating other apps' processes, which could lead to data loss, instability, or denial-of-service attacks. It also improves system stability by limiting an app's ability to interfere with other apps' processes.
ADDITIONAL RESTRICTIONS
To further enhance user experience and security, Android 14 imposes additional restrictions on starting activities from the background. Apps must meet specific criteria to launch activities while in the background. This change prevents apps from disrupting the user with unexpected activity launches and ensures that background activities do not interfere with the user’s current task.
This builds upon previous Android versions that aimed to limit disruptive background activity and enhance the user experience. The new restrictions in Android 14 make it even more challenging for apps to launch activities without direct user interaction.
The Android documentation helps clarifying two cases when launching from the background is allowed under the new rules.
Also, see this doc page.
Some code around this feature:
- First, you won't get an exception if you try to start an activity from the background, violating this restriction. Instead, you'll get a warning like this
Background activity launch blocked
I've created anApp
and itsCompanion
for your convenience. To exercise the new feature, refer to this piece of code .
It works as follows:
-
App
sends a Pending Intent toCompanion
. ThenApp
goes in the background. -
Companion
receives the Pending Intent and, after 10 seconds, executes it, staying in the foreground. - If you don't set the
setPendingIntentBackgroundActivityStartMode
flag mode whenCompanion
executes the intent, the target activity is triggered but not brought up. The target activity is shown with the flag set instead, despite its app being in the background.
On Exact Alarms
Another critical update is the default denial of exact alarms, which is applied to all apps running on Android 14. See https://developer.android.com/about/versions/14/behavior-changes-all#schedule-exact-alarms and also here https://developer.android.com/about/versions/14/changes/schedule-exact-alarms
Also, recall what Android 13 already added to this topic (https://developer.android.com/about/versions/13/features#use-exact-alarm-permission) and its impact on apps targeting the Play Store.
On Android 14, apps can no longer schedule exact alarms by default. More precisely, the SCHEDULE_EXACT_ALARM permission is no longer pre-granted to most newly installed apps targeting Android 13 and higher—it is denied by default.
Exact alarms can wake the device from sleep and consume significant power, so restricting their use helps extend battery life. Developers who require exact alarms must request special permission from the user. While some apps might require precise timing for specific functionalities, most can adapt to using inexact alarms without significant impact. Developers can request permission for SCHEDULE_EXACT_ALARM if necessary, but they must provide a clear justification for its use.
This means that alarms set using setExact()
or setExactAndAllowWhileIdle()
will no longer fire at the precise time specified unless the app has been granted special permission or meets specific exemption criteria.
Explore Alternative Approaches - Investigate alternative methods for achieving the desired functionality without relying on exact alarms, such as using WorkManager or JobScheduler for background tasks.
However, let's see our sample app's behavior when it tries setting an exact alarm.
I've added a <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
.
Also, I noticed that it does not appear as a standard runtime permission;
Rather, it looks like one of those permissions that require special attention by the user
So, let me grant it ( manually ). Once set, an Advanced section in the App Info reminds us that Alarms are allowed
It's all set! Here I'm firing the alarm
Note that
-
Trying to set the exact alarm without the proper permission in place returns
Caller com.ndzl.a14challenger needs to hold android.permission.SCHEDULE_EXACT_ALARM or android.permission.USE_EXACT_ALARM to set exact alarms.
-
One code line before, I had to assert
canScheduleExactAlarms()
-
Exact Alarms work through a system component invocation, which is triggered by a pending intent
-
At the scheduled time, a specific broadcast receiver (
ExactAlarmReceiver
) is invoked -
The broadcast receiver is manifest-declared
-
In my case, I set the
android.app.AlarmManager.RTC_WAKEUP
type of alarm, which wakes up the device in case it's sleeping
Once scheduled, alarms are managed by the OS, so you can even list them adb shell dumpsys alarm
From a debugging perspective, you'll see this.
In the code, I set 8.000ms as the trigger delay
and this is when it was actually fired (8.025ms later)
In a nutshell, I've shown how you should tweak your code to keep on using exact alarms.
Queued Broadcasts
Earlier in this blog, I covered the cached states, and I'm adding a deep dive into how context-registered broadcasts are queued while apps are cached. According to Google documentation - the system may place context-registered broadcasts in a queue when these broadcasts are set for delivery to an app that's in the cached state.
First, we need to understand Context-Registered Broadcasts. These broadcasts are registered dynamically within an app's context (using registerReceiver()) rather than being declared statically in the manifest. Also, Context-registered receivers receive broadcasts as long as their registering context is valid.
For example, if you register within an Activity context, you receive broadcasts as long as the activity is not destroyed. If you register with the Application context, you receive broadcasts as long as the app runs. When the app is not running, the system might queue these broadcasts until the app starts again, providing a clear understanding of the system's behavior.
However, witnessing this queuing feature in action is more complex than one might think. It requires the app to be in a cached state without being killed. I used a couple of tricks that worked for me:
- I set the battery at a lower level
adb shell dumpsys battery set level 12
- I opened several apps and used them intensively, e.g. watching videos on YouTube.
More or less consistently, this sent my A14Challenger app into the cached state (counters stopped adding up). Not all types of broadcasts are subject to this delayed process; as an example, Google cites less important system broadcasts such as ACTION_SCREEN_ON are deferred while the app is in a cached state.
Let's read together the following logcat where I captured a queued broadcast
- At 17:27:56, my sample app entered the background. Then I disconnected the device from the power cable and manually switched off the screen.
- Soon after, I got the SCREEN_OFF event. Recall that SCREEN_OFF means "when the device becomes non-interactive."
- Before 17:30:08, I turned the device on and held on to the launcher for a while - this doesn't get logged
- At 17:30:08, I switched to the A14Challenger app, which was still in the background. Only then were the SCREEN_OFF and SCREEN_ON events delivered to my app almost concurrently.
Refer here to replicate this behavior.
It's also relevant to note that essential broadcasts declared in the manifest temporarily remove apps from the cached state for delivery.
Typed Foreground Services
Android 14 requires apps to declare specific foreground service types to improve clarity and control over foreground services. Foreground services are used for tasks that need to be immediately noticeable to the user, such as ongoing notifications or critical operations. By requiring apps to specify the type of foreground service, Android 14 ensures that these services are used appropriately and that users better understand why a service is running.
Mandatory Foreground Service Type Declaration actions:
- When starting a foreground service using startForeground(), apps must now provide one or more foreground service types from the ServiceInfo class.
- These types categorize the service based on its primary function, such as FOREGROUND_SERVICE_TYPE_CAMERA, FOREGROUND_SERVICE_TYPE_LOCATION, or FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK.
- The complete list of types is published here.
- Additionally, Android 14 introduces foreground service types for health and remote messaging use cases. The system also reserves new types for short services, special use cases, and system exemptions.
When a use case in your app isn't associated with any of these types, Google recommends that you migrate your logic to use WorkManager or user-initiated data transfer jobs.
The code around this line shows how a typed FGS must be managed when targeting Android 14 API level. However, the critical difference is in the Manifest.xml
Where the type declaration occurs.
OpenJDK Alignment
The newly introduced alignment to OpenJDK 17 might cause some Android APIs to execute differently than in the past. Google is documenting some of these changes in OpenJDK 17 updates-1 and OpenJDK 17 updates-2.
The most relevant changes could affect
-
Sealed Classes: Enhanced control over class hierarchies and inheritance.
-
Pattern Matching for
switch
: More expressive and concise switch statements. -
Pattern Matching for
instanceof
, which allows an object to be treated as having a specific type in an instanceof without any additional variables, see here. -
Records: Simplified creation of data classes.
-
Text Blocks: Improved handling of multi-line strings. Appreciate the improvement here.
-
UUID handling
-
Regular Expressions: changes about IllegalArgumentException.
UUIDs
For the following input string, for instance,
You'll get different exceptions when targeting:
- Android 11 (aligned to Java8): a
java.lang.IllegalStateException
is raised,Caused by: java.lang.NumberFormatException: For input string: " 123e4567" under radix 16
. So it's inovking another exception type and misleading the developer. - Android 14: a
java.lang.IllegalStateException
is raised,Caused by: java.lang.IllegalArgumentException: UUID string too large
. This is focused on the UUID API and might be more meaningful to the developer.
Regex
I've already mentioned the java.lang.IllegalStateException
regarding UUID, but it also involves how regular expressions raise exceptions.
In the following lines, I explore some of these changes by comparing the behavior of two apps that were built with different target API levels.
To compare the different behaviors of JDK 17 vs. JDK 8, I used two different modules aligned to the respective language versions.
Module 'app', targeting API 34 with this reference code
be mindful also to set
Module 'oldApiTarget', targeting API 27 with this reference code
Unfortunately, at the time of writing, Kotlin is not leveraging JDK17 for Regex APIs, due to compatibility reasons, according to some online sources.
Running a similar code in native Java returns a different exception and is likely aligned to JDK17, given the module settings above.
Regex exceptions linked to Group references
This Java snippet, for instance, returns the same exception in both JDK 8 and 17
On the converse, the following piece of code returns a different exception at a lower level
JDK 17:
JDK 8:
Such were examples I could generate by myself with AI support. Google is not sharing specific code snippets that might further fit the adoption of the new JDK 17. I'm going to update this section with any new references I come across.
Android for Enterprise
Finally, with this article, Google is sharing what news is coming with Android 14 regarding the Enterprise APIs. Two main topics got updates:
- Contacts: two new fields allow personal apps to list all work profile contacts and phone numbers as long as the cross-profile contacts policy in DevicePolicyManager allows it.
- Ultra Wideband: UWB is a radio technology that can use a very low energy level for short-range, high-bandwidth communications over a large portion of the radio spectrum. Starting in Android 14 (API level 34), a device or profile owner can disallow UWB on an organization-owned device by applying the DISALLOW_ULTRA_WIDEBAND_RADIO user restriction with DevicePolicyManager.addUserRestriction().
Conclusion
In this year's blog on Android 14, I highlighted the features and changes that could most affect our enterprise developers. When you can port your applications to this Android version, please check the list of changes that apply to all apps since they will be subject to them. Except for a few topics (minimum installable target API level, and exact alarms), such a list includes some far-fetched cases that are less likely to impact enterprise use cases. For most cases, Android 14 looks like a good target version to rely on, and my invite is to move to it as soon as possible and get in touch with me for any questions.
Also, remember that Android 14 will be the final upgrade for SD660 chipset-based devices; this is another reason to adopt it and get the most out of it. E.g. this limitation applies to best sellers like TC72/77, TC21/26, and so on
Check the complete list here
Thanks for choosing Zebra, and... Happy Coding!
Nicola De Zolt L.
Nicola De Zolt
.