Bug 1902077 - Work around a battery usage misattribution bug in Android 14. r=jolin,geckoview-reviewers,owlish a=RyanVM

Android is getting confused about which process is owning a media resource;
in different places it stores either our media process's pid or our tab process's pid.
Then, when our processes shut down (or rather when they get killed by Android), this
confusion leads Android to do improper cleanup of its video resource accounting.
This causes it to keep increasing the "video" time for Firefox (in
`adb shell dumpsys batterystats --usage`) even while no Firefox processes
are running. This in turn causes the "Battery" UI in Android's system preferences to
show large values for Firefox.

To work around this attribution error, we try to ensure that Android tracks the
same PID in all the places.
Specifically, the problem is that the constructor of
`android::MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy`
is getting the owning PID from `AIBinder_getCallingPid()`. The Firefox media process
calls `MediaCodec.createByCodecName` under a binder transaction from the tab process,
specifically while the tab process is calling `ICodec$Stub$Proxy.configure` inside
`CodecProxy.init`.

This fix makes it so that `AIBinder_getCallingPid()` returns the media process's PID,
by wrapping the call to `MediaCodec.createByCodecName` in calls to
`Binder.clearCallingIdentity` / `Binder.restoreCallingIdentity`. The Android
documentation says the following about `clearCallingIdentity`:

> This can be useful if, while handling an incoming call, you will be calling
> on interfaces of other objects that may be local to your process and need to
> do permission checks on the calls coming into them (so they will check the
> permission of your own local process, and not whatever process originally
> called you).

This matches what we're trying to do here quite well.

---

Firefox media process stack showing MediaCodec.createByCodecName getting into
`AIBinder_getCallingPid()`:

```
AIBinder_getCallingPid
android::MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy(int, unsigned int, std::__1::shared_ptr<aidl::android::media::IResourceManagerClient> const&, android::MediaCodec*)[libstagefright.so]
android::MediaCodec::MediaCodec(android::sp<android::ALooper> const&, int, unsigned int, std::__1::function<android::sp<android::CodecBase>(android::AString const&, char const*)>, std::__1::function<int (android::AString const&, android::sp<android::MediaCodecInfo>*)>)
android::MediaCodec::CreateByComponentName(android::sp<android::ALooper> const&, android::AString const&, int*, int, unsigned int)
android::JMediaCodec::JMediaCodec(_JNIEnv*, _jobject*, char const*, bool, bool, int, int)
android_media_MediaCodec_native_setup(_JNIEnv*, _jobject*, _jstring*, unsigned char, unsigned char, int, int)
android.media.MediaCodec.<init>
android.media.MediaCodec.<init>
android.media.MediaCodec.createByCodecName
org.mozilla.gecko.media.LollipopAsyncCodec.<init>
```

Firefox tab process stack showing `ICodec$Stub$Proxy.configure` binder call:

```
_ioctl
ioctl
android::IPCThreadState::talkWithDriver(bool)
android::IPCThreadState::waitForResponse(android::Parcel*, int*)
android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)
android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)
android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int)
art_quick_generic_jni_trampoline
art_quick_invoke_stub
android.os.BinderProxy.transact
org.mozilla.gecko.media.ICodec$Stub$Proxy.configure
org.mozilla.gecko.media.CodecProxy.init
org.mozilla.gecko.media.RemoteManager.createCodec
org.mozilla.gecko.media.CodecProxy.create
mozilla::java::CodecProxy::Create
mozilla::RemoteVideoDecoder::Init()
```

Differential Revision: https://phabricator.services.mozilla.com/D214316
This commit is contained in:
Jeff Muizelaar 2024-06-19 23:14:24 +00:00
parent a0cbf8098c
commit f97be7a62c

View file

@ -8,6 +8,7 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -134,7 +135,18 @@ import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
}
/* package */ LollipopAsyncCodec(final String name) throws IOException {
mCodec = MediaCodec.createByCodecName(name);
// Create the codec.
// We wrap the call to MediaCodec.createByCodecName in a pair of
// clearCallingIdentity / restoreCallingIdentity, so that the resource
// gets attributed to this process and not to whichever process was calling us.
// This works around a battery usage attribution bug in Android 14+,
// see bug 1902077.
final long token = Binder.clearCallingIdentity();
try {
mCodec = MediaCodec.createByCodecName(name);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override