[go: up one dir, main page]

Page MenuHomePhabricator

Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin
ClosedPublic

Authored by RyanVM on Fri, Oct 9, 1:02 AM.
Referenced Files
F84504799: D332260.1792391142.diff
Sun, Oct 18, 6:25 AM
F84498372: D332260.1792384201.diff
Sun, Oct 18, 4:30 AM
F84488367: D332260.1792373075.diff
Sun, Oct 18, 1:24 AM
F84469226: D332260.1792350632.diff
Sat, Oct 17, 7:10 PM
Unknown Object (File)
Thu, Oct 15, 8:14 AM
Unknown Object (File)
Thu, Oct 15, 7:25 AM
Unknown Object (File)
Thu, Oct 15, 5:32 AM
Unknown Object (File)
Thu, Oct 15, 3:32 AM

Details

Summary

The provisioning request and its response now run on the handler thread that resumes the pending session
operations, instead of on AsyncTask's shared executor followed by a post to the main thread. Resuming the
pending sessions stops when one of them restarts provisioning, so that the new request can run on the same
thread. A response arriving after release() is still dropped.

Diff Detail

Event Timeline

RyanVM created this revision.
phab-bot changed the visibility from "Custom Policy" to "Public (No Login Required)".Fri, Oct 9, 1:02 AM
phab-bot changed the edit policy from "Custom Policy" to "Restricted Project (Project)".
phab-bot removed a project: secure-revision.

(Reviewing Diff 1400988)
The patch looks sound: I found nothing to flag. Responses still arrive on the main thread and are still dropped after release(), as before. The ThreadUtils.postToUiThread handoff also makes mResponseBody safely visible on the main thread. AsyncTask.cancel(true) also did nothing to stop an HttpURLConnection request already in flight, so losing the thread interrupt doesn't change behavior in practice. A dedicated thread is also an improvement: a stalled request no longer blocks the app-wide serial AsyncTask executor.


Automated review completed with no comments.
RyanVM updated this revision to Diff 1400991.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
555

cancel() now only sets a flag, so after release() the in-flight provisioning POST keeps running until it finishes or times out. During that time the worker thread keeps holding the bridge, because PostRequestTask is still a non-static inner class. The old cancel(true) at least interrupted the thread. To actually abort the request, save the HttpURLConnection in a volatile field and call disconnect() on it from cancel(). Also check mCancelled in run() before posting to the UI thread.

RyanVM added inline comments.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
555

Keeping this as is: cancel(true) interrupted the thread, but an interrupt doesn't abort blocking HttpURLConnection I/O, so the old task also ran the POST to completion or timeout. The posted result checks mCancelled before touching the bridge.

RyanVM updated this revision to Diff 1401066.
RyanVM requested review of this revision.Fri, Oct 9, 2:58 AM
RyanVM updated this revision to Diff 1401107.
RyanVM retitled this revision from WIP: Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin to Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin.
RyanVM added reviewers: jolin, geckoview-reviewers.

Happy to re-review if the comment makes sense.

mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
726

We don't need a new thread that later creates a separate thread to resume tasks (resumePendingOperations).

Can we re-purpose our existing handler so that we can provision first (making sure that mHandler has a thread created first, before resumePendingOperations?

mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
726

To clarify, my understanding is that the provisioning task is dependant on the pending operations task, so we can make them sequential on the same thread without creating a second one.

RyanVM added inline comments.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
726

Provisioning now runs on mHandler's thread, and the pending operations follow it there. Because they share that thread now, resuming the pending sessions stops when one of them restarts provisioning, so the new provisioning request isn't stuck behind them.

RyanVM edited the summary of this revision. (Show Details)
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
609–611

There is a check-then-act race between this mCancelled check and release(). release() runs on another thread, the caller's binder thread via RemoteMediaDrmBridgeStub/MediaDrmProxy, not on PendingSessionOpsThread. If it runs after the check passes, onProvisionResponse() keeps going while the bridge is torn down:

  • provideProvisionResponse() dereferences a null mDrm. That NPE is not caught, so it is an uncaught exception on the HandlerThread and crashes the process.
  • resumePendingOperations() → getHandler() lazily creates a new HandlerThread after release() has already nulled and quit the old one. Nothing ever quits the new thread, so it leaks.

Fix by making delivery and teardown mutually exclusive. Either run release()'s teardown on the handler thread, or guard the mCancelled check plus onProvisionResponse() and the cancel/teardown in release() with the same lock. Also make getHandler() refuse to recreate the thread once released.

RyanVM added inline comments.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
609–611

Worth doing, but adding it as a third patch on the stack rather than scope-creeping this one more.

RyanVM updated this revision to Diff 1407043.
RyanVM retitled this revision from Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin to WIP: Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
609–611

There is a check-then-act race here. release() gets called on the binder thread (RemoteMediaDrmBridgeStub.release()), while this code runs on PendingSessionOpsThread. If release() runs after the mCancelled check but before or during onProvisionResponse(), two things can go wrong:

  • provideProvisionResponse() dereferences a null mDrm. The resulting NPE is not caught and kills the thread.
  • resumePendingOperations() calls getHandler() after release() has nulled mHandler. That lazily starts a new HandlerThread, which nothing ever quits.

The volatile flag doesn't give the "late response is ignored" guarantee. Make the cancel and the delivery atomic: for example, guard both release() and this check-and-deliver block with the same lock, or recheck mDrm/cancellation under that lock inside onProvisionResponse(). Also make getHandler() refuse to recreate the thread after release.

RyanVM added inline comments.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridge.java
609–611

Same reply as the last time you brought this up.

RyanVM updated this revision to Diff 1407922.
RyanVM updated this revision to Diff 1407989.
RyanVM retitled this revision from WIP: Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin to Bug 2079863 - Replace the deprecated AsyncTask in GeckoMediaDrmBridge. r?#geckoview-reviewers,jolin.
This revision is now accepted and ready to land.Thu, Oct 15, 8:21 PM

Thanks a lot for the patch!