The Crux of Android 14 Application Migration


First I would like to give an overview of the Meraki Systems Manager (SM) application. Systems Manager is Meraki’s endpoint management product. We support management for many different platforms, including iOS, Android, macOS, and Windows. “Managing” a device can mean monitoring its online status, pushing profiles and apps to it, and/or enforcing security policies, among other things. With Systems Manager, this management all happens through Meraki’s online interface called Dashboard. Examples and code snippets mentioned in this blog are more specific to the Android SM application.

Migration of applications to any SDK mainly includes 2 tasks from the developer’s perspective. One is – how the application behaves when installed on a device with an Android version other than the target SDK of the app. And secondly, how the app will behave when the target SDK is changed. Developers need to understand what new features, or updates of any existing feature, and its impact on the application are.

This document focuses on some of the changes impacting developers with Android 14 migration. It also covers migration of the Systems Manager app to Android 14, and challenges encountered during the migration and testing.

Font Scaling

In earlier versions of Android i.e., 13 Non-linear font scaling was supported up to 130% but in Android 14, it is supported up to 200% which can impact the UI of the application. In the application if font dimensions are declared using sp (scaled pixel) units there are chances of minimal impact on the application because Android framework would apply these scaling factors. Because of nonlinear scaling of font density scaling will not be accurate.
Key points

  • TypedValue.applyDimension() to convert from sp units to pixels.
  • TypedValue.deriveDimension() to convert pixels to sp
  • LineHeight units should be specified in sp to manage proportion along with text size.

Background Process Limitation

Android OS is self sufficient to manage the resources efficiently by improvising performance as well. One of the pointers to achieve the same is by caching applications in the background and only when the system needs memory these applications will be removed from memory. All applications should comply with Google Play policy and hence killing of processes of other applications are strictly restricted in Android 14. Hence killBackgroundProcessess() can kill only the background processes of your own application.

Foreground Service Types

In Android 10, a new attribute was introduced to specify service type for foreground services. When using location information in the foreground service it was required to specify the type as “location”. Whereas in Android 11, mentioning service type for usage of camera or microphone in foreground service was mandated. But in Android 14 or above, all foreground services must be declared with their service types.

Some of the new service types were also introduced in Android 14 – health, remoteMessaging, shortService, specialUse and systemExempted. If service isn’t associated with any of the types specified, then it is recommended to change logic to use Workmanager or user-initiated data transfer jobs. MissingForegroundServiceTypeException will be thrown by the system in case service type is not specified.

Service type permissions need to be declared along with specifying the type in service.

      

      

Limitations on Implicit Intent and Pending Intent

Implicit intents are only delivered to exported components. This restriction ensures the application’s implicit intents aren’t used by any other malicious apps. Also, all mutable pending intent must specify a component or package information to the intent, if not the system throws an exception.

Implicit intent should be export similar to this:

 
   
      
      
   

If pending intent should be mutable, then component info must be specified.

val flags = if (MerakiUtils.isApi31OrHigher()) {
   PendingIntent.FLAG_MUTABLE
} else {
   PendingIntent.FLAG_UPDATE_CURRENT
}

val pendingIntent = PendingIntent.getActivity(
   this,
   0,
   Intent(context, KioskActivity::class.java).apply {
      putExtra(ACTION, KioskActivity.BREAK_OUT_SINGLE_APP)
   },
   flags
)

Export behavior to be specified for Runtime-registered broadcasts

Prior to Android 13, there were no restrictions on sending broadcasts to a dynamically registered receiver when it is guarded by signature permission. Whereas in Android 13, aiming at making runtime receivers safe, an optional flag was introduced to specify whether the receiver is exported and visible to other applications. To protect apps from security vulnerabilities, in Android 14 or above context-registered receivers are required to specify a flag RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED to indicate whether receiver should be exported or not to all other apps on the device. This is exempted for system broadcasts.

ContextCompat.registerReceiver(
   requireContext(), receiver,intentFilter(),
   ContextCompat.RECEIVER_NOT_EXPORTED

Non-Dismissable foreground notifications

In Android 14 or higher, foreground notification can be dismissed by the user. But exceptions have been provided for Device policy Controller (DPC) and supporting packages for enterprise.

JobScheduler reinforces callback and network behavior

Prior to Android 14, for any job running for too long, it would stop and fail silently. When App targets Android 14 and if the job exceeds the guaranteed time on the main thread, the app triggers an ANR with an error message “No response to onStartJob” or “No response to onStopJob”. It is suggested to use WorkManager for any asynchronous processing.

Changes specific to Android Enterprise

Android Enterprise is a Google-led initiative to enable the use of Android devices and apps in the workplace. It is also termed as Android for Work. It helps to manage and distribute private apps alongside public apps, providing a unified enterprise app store experience for end users.

GET_PROVISIONING_MODE intent behavior

For signing in with a Google account, GET_PROVISIONING_MODE was introduced in Android 12 or higher. In Android 14 or higher, DPC apps receive this intent which can carry the information to support either Fully managed mode or work profile mode.

wipeDevice – for resetting device

Scope of wipeData is now restricted to profile owners only. For apps targeting Android 14 or higher, this method would throw system error when called in device owner mode. New method wipeDevice to be used for resetting the device along with USES_POLICY_WIPE_DATA permission.

Newly added fields and methods

ContactsContract.Contacts#ENTERPRISE_CONTENT_URI
ContactsContract.CommonDataKinds.Phone#ENTERPRISE_CONTENT_URI

When cross-profile contacts policy is allowed in DevicePolicyManager, these fields can be used for listing all work profile contacts and phone numbers from personal apps along with READ_CONTACTS permission.

To support setting contact access policy and callerID, below methods are newly added;

setManagedProfileContactsAccessPolicy
getManagedProfileContactsAccessPolicy
setManagedProfileCallerIdAccessPolicy
getManagedProfileCallerIdAccessPolicy

Deprecated methods

Below methods are deprecated and as an alternative methods specified in the previous section should be used.

DevicePolicyManger#setCrossProfileContactsSearchDisabled
DevicePolicyManger#getCrossProfileContactsSearchDisabled
DevicePolicyManger#setCrossProfileCallerIdDisabled
DevicePolicyManger#getCrossProfileCallerIdDisabled

Challenges during Meraki Systems Manager App Migration

  • To ensure there was no UI breakage, we had to recheck all the code base of xml files related to all fragments, alert dialog and text size dimensions.
  • Few APIs like wipeDevice(), were not mentioned in the Android migration 14. During the testing phase it was found that wipeData() is deprecated in Android 14 and wipeDevice() was supposed to be used for factory resetting the device successfully.
  • Profile information which can be fetched along with intent GET_PROVISIONING_MODE was also missed in the migration guide. This was found during the regression testing phase.
  • requestSingleUpdate() of location manager always requires mutable pending for location updation. But nowhere in the documentation, it is prescribed about it. Due to this there were few application crashes. Had to figure this out during application testing.

Useful links for reference

 

Share:



Source link