Merge "Only allow completion callback to be run once for PIs" into sc-qpr1-dev

This commit is contained in:
Soonil Nagarkar
2021-09-23 15:11:43 +00:00
committed by Android (Google) Code Review

View File

@@ -243,15 +243,24 @@ public class LocationProviderManager extends
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
// send() SHOULD only run the completion callback if it completes successfully. however,
// b/199464864 (which could not be fixed in the S timeframe) means that it's possible
// for send() to throw an exception AND run the completion callback. if this happens, we
// would over-release the wakelock... we take matters into our own hands to ensure that
// the completion callback can only be run if send() completes successfully. this means
// the completion callback may be run inline - but as we've never specified what thread
// the callback is run on, this is fine.
GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
mPendingIntent.send(
mContext,
0,
intent,
onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
: null,
(pI, i, rC, rD, rE) -> gatedCallback.run(),
null,
null,
options.toBundle());
gatedCallback.allow();
}
@Override
@@ -2734,4 +2743,49 @@ public class LocationProviderManager extends
}
}
}
private static class GatedCallback implements Runnable {
private @Nullable Runnable mCallback;
@GuardedBy("this")
private boolean mGate;
@GuardedBy("this")
private boolean mRun;
GatedCallback(Runnable callback) {
mCallback = callback;
}
public void allow() {
Runnable callback = null;
synchronized (this) {
mGate = true;
if (mRun && mCallback != null) {
callback = mCallback;
mCallback = null;
}
}
if (callback != null) {
callback.run();
}
}
@Override
public void run() {
Runnable callback = null;
synchronized (this) {
mRun = true;
if (mGate && mCallback != null) {
callback = mCallback;
mCallback = null;
}
}
if (callback != null) {
callback.run();
}
}
}
}