Showing posts with label signing. Show all posts
Showing posts with label signing. Show all posts

Understanding APK packaging in Android Studio 2.2

Posted by Wojtek KaliciƄski, Android Developer Advocate


Android Studio 2.2 launched recently with href="http://android-developers.blogspot.com/2016/09/android-studio-2-2.html">many
new and improved features. Some of the changes are easy to miss because they
happened under the hood in the Android Gradle plugin, such as the newly
rewritten integrated APK packaging and signing step.






APK Signature Scheme v2



With the introduction of the new href="https://source.android.com/security/apksigning/v2.html">APK Signature
Scheme v2 in Android 7.0 Nougat, we decided to rewrite how assembling APKs
works in the Android Gradle plugin. You can read all about the low-level
technical details of v2 signatures in the href="https://source.android.com/security/apksigning/v2.html">documentation,
but here's a quick tl;dr summary of the info you need as an Android app
developer:


  • The cryptographic signature of the APK that is used to verify its integrity
    is now located immediately before the ZIP Central Directory.
  • The signature is computed and verified over the binary contents of the whole
    APK file, as opposed to decompressed file contents of each file in the archive
    in v1.
  • An APK can be signed by both v1 and v2 signatures at the same time, so it
    remains backwards compatible with previous Android releases.


Why introduce this change to how Android verifies APKs? Firstly, for enhanced
security and extensibility of this new signing format, and secondly for
performance - the new signatures take significantly less time to verify on the
device (no need for costly decompression), resulting in faster app installation
times.



The consequence of this new signing scheme, however, is that there are new
constraints on the APK creation process. Since only uncompressed file contents
were verified in v1, that allowed for quite a lot of modifications to be made
after APK signing - files could be moved around or even recompressed. In fact,
the zipalign tool which was part of the build process did exactly
that - it was used to align ZIP entries on correct byte boundaries for improved
runtime performance.



Because v2 signatures verify all bytes in the archive and not individual ZIP
entries, running zipalign is no longer possible after
signing
. That's why compression, aligning and signing now happens in a
single, integrated step of the build process.



If you have any custom tasks in your build process that involve tampering with
or post-processing the APK file in any way, please make sure you disable them or
you risk invalidating the v2 signature and thus making your APKs incompatible
with Android 7.0 and above.



Should you choose to do signing and aligning manually (such as from the command
line), we offer a new tool in the Android SDK, called href="https://developer.android.com/studio/command-line/apksigner.html?utm_campaign=android_discussion_api_111016&utm_source=anddev&utm_medium=blog">apksigner,
that provides both v1 and v2 APK signing and verification. Note that you need to
run zipalign before running apksigner
if you are using v2 signatures. Also remember the jarsigner tool
from the JDK is not compatible with Android v2 signatures, so you can't use it
to re-sign your APKs if you want to retain the v2 signature.


In case you want to disable adding v1 or v2 signatures when building with the
Android Gradle plugin, you can add these lines to your href="https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.SigningConfig.html">signingConfig
section in build.gradle:

class="prettyprint">v1SigningEnabled false
v2SigningEnabled false


Note: both signing schemes are enabled by default in Android Gradle plugin 2.2.


Release builds for smaller APKs



We took this opportunity when rewriting the packager to make some optimizations
to the size of release APKs, resulting in faster downloads, href="http://android-developers.blogspot.co.uk/2016/07/improvements-for-smaller-app-downloads.html">smaller
delta updates on the Play Store, and less wasted space on the device. Here
are some of the changes we made:


  • Files in the archive are now sorted to minimize differences between APK
    builds.
  • All file timestamps and metadata are zeroed out.
  • Level 6 and level 9 compression is checked for all files in parallel and the
    optimal one is used, i.e. if L9 provides little benefit in terms of size, then
    L6 may be chosen for better performance
  • Native libraries are stored uncompressed and page aligned in the APK. This
    brings support for the android:extractNativeLibs="false" option
    from Android 6.0 Marshmallow and lets applications use less space on the device
    as well as generate smaller updates on the Play Store
  • Zopfli compression is not used to better support Play Store update
    algorithms. It is not recommended to recompress your APKs with Zopfli.
    Pre-optimizing individual resources such as PNG files in your projects is still
    fine and recommended.


These changes help make your releases as small as possible so that users can
download and update your app even on a slower connection or on less capable
devices. But what about debug builds?


Debug builds for installation speed



When developing apps you want to keep the iteration cycle fast - change code,
build, and deploy on a connected device or emulator. Since Android Studio 2.0
we've been working to make all the steps as fast as possible. With Instant Run
we're now able to update only the changed code and resources during runtime,
while the new Emulator brings multi-processor support and faster ADB speeds for
quicker APK transfer and installation. Build improvements can cut that time even
further and in Android Studio 2.2 we're introducing incremental packaging and
parallel compression for debug builds. Together with other features like
selectively packaging resources for the target device density and ABI this will
make your development even faster.



A word of caution: the APK files created for Instant Run or by invoking a debug
build are not meant for distribution on the Play Store! They contain additional
instrumentation code for Instant Run and are missing resources for device
configurations other than the one that was connected when you started the build.
Make sure you only distribute release versions of the APK which you can create
using the Android Studio href="https://developer.android.com/studio/publish/app-signing.html?utm_campaign=android_discussion_api_111016&utm_source=anddev&utm_medium=blog#release-mode">Generate
Signed APK command or the assembleRelease Gradle task.




Things That Cannot Change

[This post is by Dianne Hackborn, whose fingerprints can be found all over the Android Application Framework — Tim Bray]

Sometimes a developer will make a change to an application that has surprising results when installed as an update to a previous version — shortcuts break, widgets disappear, or it can’t even be installed at all. There are certain parts of an application that are immutable once you publish it, and you can avoid surprises by understanding them.

Your package name and certificate

The most obvious and visible of these is the “manifest package name,” the unique name you give to your application in its AndroidManifest.xml. The name uses a Java-language-style naming convention, with Internet domain ownership helping to avoid name collisions. For example, since Google owns the domain “google.com”, the manifest package names of all of our applications should start with “com.google.” It’s important for developers to follow this convention in order to avoid conflicts with other developers.

Once you publish your application under its manifest package name, this is the unique identity of the application forever more. Switching to a different name results in an entirely new application, one that can’t be installed as an update to the existing application.

Just as important as the manifest package name is the certificate that application is signed with. The signing certificate represents the author of the application. If you change the certificate an application is signed with, it is now a different application because it comes from a different author. This different application can’t be uploaded to Market as an update to the original application, nor can it be installed onto a device as an update.

The exact behavior the user sees when installing an application that has changed in one of these two ways is different:

  • If the manifest package name has changed, the new application will be installed alongside the old application, so they both co-exist on the user’s device at the same time.

  • If the signing certificate changes, trying to install the new application on to the device will fail until the old version is uninstalled.

If you change the signing certificate of your application, you should always change its manifest package name as well to avoid failures when it’s installed. In other words, the application coming from a different author makes it a different application, and its package name should be changed appropriately to reflect that. (Of course it’s fine to use the same package name for the development builds of your app signed with your test keys, because these are not published.)

Your AndroidManifest.xml is a public API

More than just your package name that is immutable. A major function of the AndroidManifest.xml is essentially to declare a public API from your application for use by other applications and the Android system. Every component you declare in the manifest that is not private (that is whose android:exported state is true) should be treated as a public API and never changed in a way that breaks compatibility.

A subtle but important aspect of what constitutes a break in compatibility is the android:name attribute of your activity, service, and receiver components. This can be surprising because we think of android:name as pointing to the private code implementing our application, but it is also (in combination with the manifest package name) the official unique public name for that component, as represented by the ComponentName class.

Changing the component name inside of an application can have negative consequences for your users. Some examples are:

  • If the name of a main activity of your application is changed, any shortcuts the user made to it will no longer work. A shortcut is an Intent that directly specifies the ComponentName it should run.

  • If the name of a service implementing a Live Wallpaper changes, then a user who has enabled your Live Wallpaper will have their wallpaper revert to the system default when getting the new version of your app. The same is true for Input Methods, Accessibility Services, Honeycomb’s new advanced Widgets, and so on.

  • If the name of a receiver implementing a Device Admin changes, then as with the live wallpaper example, the device admin will be disabled when the application is updated. This also applies to other kinds of receivers, such as App Widgets.

These behaviors are an outcome of how the Intent system is used on Android. There are two main kinds of Intents:

  • Implicit Intents only specify “what” they should match, using actions, categories, data, MIME types, and so on. The exact components that they will find are only determined at run-time, by the Package Manager matching it against the current applications.

  • Explicit Intents specify a single explicit “who” they should match, through a ComponentName. Regardless of whatever else is in the Intent, it is only associated with the exact manifest package name and class name as given in its ComponentName.

Both of these types of Intents are important to how Android interacts with your application. A typical example of this is how users browse and select live wallpapers.

To let the user pick a live wallpaper, the first thing Android must do is show them a list of the available live wallpaper services. It does this by building an implicit Intent with the appropriate action for a live wallpaper and asking the Package Manager for all services that support this Intent. The result is then the list of live wallpapers shown to the user.

When the user actually selects a specific live wallpaper they want to use, however, Android now must build an explicit Intent that identifies that particular live wallpaper. This is what is handed to the WallpaperManager to tell it which wallpaper to show.

This is why changing the name of the component in your manifest will cause the wallpaper to disappear: the explicit Intent that was previously saved is now invalid because the ComponentName it references no longer exists. There is no information available to indicate what the new name of the component is. (For example consider if your application had two different live wallpaper services the user could select.) Instead, Android must treat that live wallpaper as uninstalled and revert to its default wallpaper.

This is how input methods, device administrators, account managers, app widgets, and even application shortcuts work. The ComponentName is the public unique name of the components you declare in your manifest, and must not change if they are visible to other applications.

In conclusion: There are some parts of your application that can not change. Please be careful.