Merge "Retrying the remoteview application on the ui thread" into oc-dev

This commit is contained in:
Selim Cinek
2017-06-20 22:40:59 +00:00
committed by Android (Google) Code Review
2 changed files with 100 additions and 7 deletions

View File

@@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
public class NotificationInflater { public class NotificationInflater {
public static final String TAG = "NotificationInflater";
@VisibleForTesting @VisibleForTesting
static final int FLAG_REINFLATE_ALL = ~0; static final int FLAG_REINFLATE_ALL = ~0;
private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0; private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
@@ -315,7 +316,8 @@ public class NotificationInflater {
return cancellationSignal; return cancellationSignal;
} }
private static void applyRemoteView(final InflationProgress result, @VisibleForTesting
static void applyRemoteView(final InflationProgress result,
final int reInflateFlags, int inflationId, final int reInflateFlags, int inflationId,
final ExpandableNotificationRow row, final ExpandableNotificationRow row,
final boolean redactAmbient, boolean isNewView, final boolean redactAmbient, boolean isNewView,
@@ -325,6 +327,7 @@ public class NotificationInflater {
NotificationViewWrapper existingWrapper, NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations, final HashMap<Integer, CancellationSignal> runningInflations,
ApplyCallback applyCallback) { ApplyCallback applyCallback) {
RemoteViews newContentView = applyCallback.getRemoteView();
RemoteViews.OnViewAppliedListener listener RemoteViews.OnViewAppliedListener listener
= new RemoteViews.OnViewAppliedListener() { = new RemoteViews.OnViewAppliedListener() {
@@ -343,12 +346,31 @@ public class NotificationInflater {
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
runningInflations.remove(inflationId); // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
handleInflationError(runningInflations, e, entry.notification, callback); // actually also be a system issue, so let's try on the UI thread again to be safe.
try {
View newView = existingView;
if (isNewView) {
newView = newContentView.apply(
result.packageContext,
parentLayout,
remoteViewClickHandler);
} else {
newContentView.reapply(
result.packageContext,
existingView,
remoteViewClickHandler);
}
Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
e);
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
handleInflationError(runningInflations, e, entry.notification, callback);
}
} }
}; };
CancellationSignal cancellationSignal; CancellationSignal cancellationSignal;
RemoteViews newContentView = applyCallback.getRemoteView();
if (isNewView) { if (isNewView) {
cancellationSignal = newContentView.applyAsync( cancellationSignal = newContentView.applyAsync(
result.packageContext, result.packageContext,
@@ -620,14 +642,16 @@ public class NotificationInflater {
} }
} }
private static class InflationProgress { @VisibleForTesting
static class InflationProgress {
private RemoteViews newContentView; private RemoteViews newContentView;
private RemoteViews newHeadsUpView; private RemoteViews newHeadsUpView;
private RemoteViews newExpandedView; private RemoteViews newExpandedView;
private RemoteViews newAmbientView; private RemoteViews newAmbientView;
private RemoteViews newPublicView; private RemoteViews newPublicView;
private Context packageContext; @VisibleForTesting
Context packageContext;
private View inflatedContentView; private View inflatedContentView;
private View inflatedHeadsUpView; private View inflatedHeadsUpView;
@@ -636,7 +660,8 @@ public class NotificationInflater {
private View inflatedPublicView; private View inflatedPublicView;
} }
private abstract static class ApplyCallback { @VisibleForTesting
abstract static class ApplyCallback {
public abstract void setResultView(View v); public abstract void setResultView(View v);
public abstract RemoteViews getRemoteView(); public abstract RemoteViews getRemoteView();
} }

View File

@@ -24,12 +24,17 @@ import static org.mockito.Mockito.verify;
import android.app.Notification; import android.app.Notification;
import android.content.Context; import android.content.Context;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest; import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.FlakyTest; import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import com.android.systemui.R; import com.android.systemui.R;
@@ -45,7 +50,9 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@SmallTest @SmallTest
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -141,6 +148,41 @@ public class NotificationInflaterTest extends SysuiTestCase {
Assert.assertNull(mRow.getEntry().getRunningTask()); Assert.assertNull(mRow.getEntry().getRunningTask());
} }
@Test
public void testInflationIsRetriedIfAsyncFails() throws Exception {
NotificationInflater.InflationProgress result =
new NotificationInflater.InflationProgress();
result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1);
NotificationInflater.applyRemoteView(result,
NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
new NotificationInflater.InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
Exception e) {
countDownLatch.countDown();
throw new RuntimeException("No Exception expected");
}
@Override
public void onAsyncInflationFinished(NotificationData.Entry entry) {
countDownLatch.countDown();
}
}, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
new NotificationInflater.ApplyCallback() {
@Override
public void setResultView(View v) {
}
@Override
public RemoteViews getRemoteView() {
return new AsyncFailRemoteView(mContext.getPackageName(),
R.layout.custom_view_dark);
}
});
countDownLatch.await();
}
@Test @Test
public void testSupersedesExistingTask() throws Exception { public void testSupersedesExistingTask() throws Exception {
@@ -199,4 +241,30 @@ public class NotificationInflaterTest extends SysuiTestCase {
mException = exception; mException = exception;
} }
} }
private class AsyncFailRemoteView extends RemoteViews {
Handler mHandler = new Handler(Looper.getMainLooper());
public AsyncFailRemoteView(String packageName, int layoutId) {
super(packageName, layoutId);
}
@Override
public View apply(Context context, ViewGroup parent) {
return super.apply(context, parent);
}
@Override
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener, OnClickHandler handler) {
mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
return new CancellationSignal();
}
@Override
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener) {
return applyAsync(context, parent, executor, listener, null);
}
}
} }