Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +32 −7 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class NotificationInflater { public static final String TAG = "NotificationInflater"; @VisibleForTesting static final int FLAG_REINFLATE_ALL = ~0; private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0; Loading Loading @@ -315,7 +316,8 @@ public class NotificationInflater { return cancellationSignal; } private static void applyRemoteView(final InflationProgress result, @VisibleForTesting static void applyRemoteView(final InflationProgress result, final int reInflateFlags, int inflationId, final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView, Loading @@ -325,6 +327,7 @@ public class NotificationInflater { NotificationViewWrapper existingWrapper, final HashMap<Integer, CancellationSignal> runningInflations, ApplyCallback applyCallback) { RemoteViews newContentView = applyCallback.getRemoteView(); RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() { Loading @@ -343,12 +346,31 @@ public class NotificationInflater { @Override public void onError(Exception e) { // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could // 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; RemoteViews newContentView = applyCallback.getRemoteView(); if (isNewView) { cancellationSignal = newContentView.applyAsync( result.packageContext, Loading Loading @@ -620,14 +642,16 @@ public class NotificationInflater { } } private static class InflationProgress { @VisibleForTesting static class InflationProgress { private RemoteViews newContentView; private RemoteViews newHeadsUpView; private RemoteViews newExpandedView; private RemoteViews newAmbientView; private RemoteViews newPublicView; private Context packageContext; @VisibleForTesting Context packageContext; private View inflatedContentView; private View inflatedHeadsUpView; Loading @@ -636,7 +660,8 @@ public class NotificationInflater { private View inflatedPublicView; } private abstract static class ApplyCallback { @VisibleForTesting abstract static class ApplyCallback { public abstract void setResultView(View v); public abstract RemoteViews getRemoteView(); } Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,17 @@ import static org.mockito.Mockito.verify; import android.app.Notification; import android.content.Context; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.systemui.R; Loading @@ -45,7 +50,9 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) Loading Loading @@ -142,6 +149,41 @@ public class NotificationInflaterTest extends SysuiTestCase { 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 public void testSupersedesExistingTask() throws Exception { Loading Loading @@ -200,4 +242,30 @@ public class NotificationInflaterTest extends SysuiTestCase { 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); } } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +32 −7 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class NotificationInflater { public static final String TAG = "NotificationInflater"; @VisibleForTesting static final int FLAG_REINFLATE_ALL = ~0; private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0; Loading Loading @@ -315,7 +316,8 @@ public class NotificationInflater { return cancellationSignal; } private static void applyRemoteView(final InflationProgress result, @VisibleForTesting static void applyRemoteView(final InflationProgress result, final int reInflateFlags, int inflationId, final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView, Loading @@ -325,6 +327,7 @@ public class NotificationInflater { NotificationViewWrapper existingWrapper, final HashMap<Integer, CancellationSignal> runningInflations, ApplyCallback applyCallback) { RemoteViews newContentView = applyCallback.getRemoteView(); RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() { Loading @@ -343,12 +346,31 @@ public class NotificationInflater { @Override public void onError(Exception e) { // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could // 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; RemoteViews newContentView = applyCallback.getRemoteView(); if (isNewView) { cancellationSignal = newContentView.applyAsync( result.packageContext, Loading Loading @@ -620,14 +642,16 @@ public class NotificationInflater { } } private static class InflationProgress { @VisibleForTesting static class InflationProgress { private RemoteViews newContentView; private RemoteViews newHeadsUpView; private RemoteViews newExpandedView; private RemoteViews newAmbientView; private RemoteViews newPublicView; private Context packageContext; @VisibleForTesting Context packageContext; private View inflatedContentView; private View inflatedHeadsUpView; Loading @@ -636,7 +660,8 @@ public class NotificationInflater { private View inflatedPublicView; } private abstract static class ApplyCallback { @VisibleForTesting abstract static class ApplyCallback { public abstract void setResultView(View v); public abstract RemoteViews getRemoteView(); } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,17 @@ import static org.mockito.Mockito.verify; import android.app.Notification; import android.content.Context; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.systemui.R; Loading @@ -45,7 +50,9 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) Loading Loading @@ -142,6 +149,41 @@ public class NotificationInflaterTest extends SysuiTestCase { 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 public void testSupersedesExistingTask() throws Exception { Loading Loading @@ -200,4 +242,30 @@ public class NotificationInflaterTest extends SysuiTestCase { 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); } } }