From 3b8e8d76315f6718a982d5e6a019b3aa4f634bcd Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Wed, 8 Jul 2020 14:09:10 +0800 Subject: [PATCH] Fix NPE when invoking Context#isUiContext Add null checks in both ContextWrapper and before obtaining ContextImpl#getOuterContext. Test: atest ContextTest#testIsUiContext_ContextWrapper fixes: 160037462 Change-Id: Ic6a71dd9ac4b195d219d6e5431f2f2b199a400fa --- core/java/android/app/ContextImpl.java | 39 +++++++++++-------- core/java/android/content/ContextWrapper.java | 3 ++ .../src/android/content/ContextTest.java | 23 +++++++++++ 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a828aac78dede..505b498e3cf6f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1900,26 +1900,31 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { - // We may override this API from outer context. - final boolean isUiContext = isUiContext() || getOuterContext().isUiContext(); - // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) { - final String errorMessage = "Tried to access visual service " - + SystemServiceRegistry.getSystemServiceClassName(name) - + " from a non-visual Context:" + getOuterContext(); - final String message = "Visual services, such as WindowManager, WallpaperService or " - + "LayoutInflater should be accessed from Activity or other visual Context. " - + "Use an Activity or a Context created with " - + "Context#createWindowContext(int, Bundle), which are adjusted to the " - + "configuration and visual bounds of an area on screen."; - final Exception exception = new IllegalAccessException(errorMessage); - StrictMode.onIncorrectContextUsed(message, exception); - Log.e(TAG, errorMessage + message, exception); + if (vmIncorrectContextUseEnabled()) { + // We may override this API from outer context. + final boolean isUiContext = isUiContext() || isOuterUiContext(); + // Check incorrect Context usage. + if (isUiComponent(name) && !isUiContext) { + final String errorMessage = "Tried to access visual service " + + SystemServiceRegistry.getSystemServiceClassName(name) + + " from a non-visual Context:" + getOuterContext(); + final String message = "Visual services, such as WindowManager, WallpaperService " + + "or LayoutInflater should be accessed from Activity or other visual " + + "Context. Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to " + + "the configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + " " + message, exception); + } } - return SystemServiceRegistry.getSystemService(this, name); } + private boolean isOuterUiContext() { + return getOuterContext() != null && getOuterContext().isUiContext(); + } + @Override public String getSystemServiceName(Class serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); @@ -2371,7 +2376,7 @@ class ContextImpl extends Context { context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); - context.mIsUiContext = isUiContext() || getOuterContext().isUiContext(); + context.mIsUiContext = isUiContext() || isOuterUiContext(); return context; } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 5dc41e483640a..89abfc95d634c 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1151,6 +1151,9 @@ public class ContextWrapper extends Context { */ @Override public boolean isUiContext() { + if (mBase == null) { + return false; + } return mBase.isUiContext(); } } diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index 17d1389a66026..777f4a3e03a8b 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.app.ActivityThread; @@ -180,4 +181,26 @@ public class ContextTest { VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); return virtualDisplay.getDisplay(); } + + @Test + public void testIsUiContext_ContextWrapper() { + ContextWrapper wrapper = new ContextWrapper(null /* base */); + + assertFalse(wrapper.isUiContext()); + + wrapper = new ContextWrapper(new TestUiContext()); + + assertTrue(wrapper.isUiContext()); + } + + private static class TestUiContext extends ContextWrapper { + TestUiContext() { + super(null /* base */); + } + + @Override + public boolean isUiContext() { + return true; + } + } }