When Android 5.0 shipped with a number of bugs and memory leaks many people complained about apparent Google QA deficiencies. Surely, the initial Lollipop ROMs shouldn’t get a green flag from QA but it looks like the problem lies much deeper in Google’s delivery process.
I don’t have enough insider knowledge to point out where exactly the organizational problem lies but based on the code analysis I can demonstrate that issues span from APIs design through implementation to testing. I am going to use Lollipop Screen Capture API as an example.
Untested code
If you are a Java programmer you’ll spot that something is wrong with this code at a glance.
private final MapmCallbacks; final int N = mCallbacks.size(); for (int i = 0; i < N; i++) { mCallbacks.get(i).onStop(); }
Yes, this code always throws an exception.
This is part of MediaProjection
class. Half of this small class is dedicated to callbacks handling but apparently no one tested this functionality! There are no automated test and the half-baked API Demo declares callback class but never uses it.
Something like that should not happen in framework code. If developers took their time to implement this non trivial feature they should also write at least some rudimentary tests and useful demos.
This would not only eliminate such an obvious errors but also spare a lot of debugging time for developers trying to use a new APIs.
Misleading documentation
The MediaProjection
class overview says:
A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities granted depend on the type of MediaProjection. A screen capture session can be started through createScreenCaptureIntent(). This grants the ability to capture screen contents, but not system audio.
Not only this is a bad technical writing but the first sentence is simply untrue.
The implementation of system audio recording looks like this:
/** * Creates an AudioRecord to capture audio played back by the system. * @hide */ public AudioRecord createAudioRecord( int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) { return null; }
and not surprisingly it doesn’t work 😉
This issue got reported on Android Issue Tracker but nothing changed in the documentation for 5.1.
SystemUI crash
The screen capture permission dialog looks like this (not a very complex UI)
In Lollipop 5.1 Android Team modified the underlying Activity
to ensure that this UI is displayed on top of other UI elements. The code quality of this change is rather bad (unused import added, could be much more generic…) but that’s not a big deal.
The problem is the following block of code:
protected void onDestroy() { super.onDestroy(); mDialog.dismiss(); }
Android Studio must have warned the developer that this may cause NullPointerException
but the warning got ignored and the code made it into production. And again, the exception is not thrown in some obscure circumstances. Selecting “Don’t show again” checkbox is all you need to do to get a SystemUI crash which sends you back to the lock screen. What’s worse, there is no way to unselect this option after you selected it once! The only way to get rid of the crash is to reinstall the app which asked for MediaProjection
.
The dialog has two buttons and one checkbox. It’s really not that hard to test all the combinations. But apparently only the “happy path” got tested and the fix went through.
Incomplete APIs
It looks like APIs of some features (e.g. video encoding with MediaCodec
and MediaMuxer
) are not really thought through and properly designed upfront. Instead with each release some new C++ features are mapped to public Java SDK and other are renamed or deprecated. This is usually accompanied with poor documentation.
e.g. MediaMuxer
API appears to be a result of a User Story which sounds something like “As a user X, I want to save audio and video data to MP4 file”. So the class has single constructor which takes a String
path argument. The user story is satisfied, sign of… done, done. But this is not a reusable API! It should at least be able to take OutputStream
or FileDescriptor
as argument. Such a requirement would be obvious if someone took their time to think about this API and properly design it. But apparently they only implemented the bare minimum required to mark the story as completed. Couple releases later the Storage Access Framework (this time a carefully designed API) is added and it can’t talk to MediaMuxer
because MediaMuxer
is too primitive.
Conclusion
One can argue that with such a large number of changes and relatively short development time bugs are inevitable. While it’s partially true, I don’t think that’s the case with many Android bugs. Carefully designed APIs and more consistent testing approach would surely spare Android Team a lot of time on hunting regressions and addressing bug reports.
Hopefully they will figure it out and “M” will deliver the awaited quality improvement.