1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content; 18 19 import static android.content.Context.OVERRIDABLE_COMPONENT_CALLBACKS; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.fail; 26 27 import android.app.WindowConfiguration; 28 import android.compat.testing.PlatformCompatChangeRule; 29 import android.content.res.Configuration; 30 import android.graphics.Rect; 31 import android.hardware.display.DisplayManager; 32 import android.platform.test.annotations.Presubmit; 33 import android.view.Display; 34 import android.window.WindowContext; 35 36 import androidx.test.core.app.ApplicationProvider; 37 import androidx.test.ext.junit.runners.AndroidJUnit4; 38 import androidx.test.filters.SmallTest; 39 40 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; 41 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.rules.TestRule; 45 import org.junit.runner.RunWith; 46 47 48 /** 49 * Build/Install/Run: 50 * atest FrameworksCoreTests:ContextWrapperTest 51 */ 52 @Presubmit 53 @SmallTest 54 @RunWith(AndroidJUnit4.class) 55 public class ContextWrapperTest { 56 @Rule 57 public TestRule compatChangeRule = new PlatformCompatChangeRule(); 58 59 /** 60 * Before {@link android.os.Build.VERSION_CODES#TIRAMISU}, {@link ContextWrapper} must 61 * register {@link ComponentCallbacks} to {@link ContextWrapper#getApplicationContext} before 62 * {@link ContextWrapper#attachBaseContext(Context)}. 63 */ 64 @DisableCompatChanges(OVERRIDABLE_COMPONENT_CALLBACKS) 65 @Test testRegisterComponentCallbacksWithoutBaseContextBeforeT()66 public void testRegisterComponentCallbacksWithoutBaseContextBeforeT() { 67 final ContextWrapper wrapper = new TestContextWrapper(null /* base */); 68 final ComponentCallbacks callbacks = new TestComponentCallbacks2(); 69 70 // It should be no-op if unregister a ComponentCallbacks without registration. 71 wrapper.unregisterComponentCallbacks(callbacks); 72 73 wrapper.registerComponentCallbacks(callbacks); 74 75 assertThat(wrapper.mCallbacksRegisteredToSuper.size()).isEqualTo(1); 76 assertThat(wrapper.mCallbacksRegisteredToSuper.get(0)).isEqualTo(callbacks); 77 78 wrapper.unregisterComponentCallbacks(callbacks); 79 80 assertThat(wrapper.mCallbacksRegisteredToSuper.isEmpty()).isTrue(); 81 } 82 83 /** 84 * After {@link android.os.Build.VERSION_CODES#TIRAMISU}, {@link ContextWrapper} must 85 * throw {@link IllegalStateException} before {@link ContextWrapper#attachBaseContext(Context)}. 86 */ 87 @Test testRegisterComponentCallbacksWithoutBaseContextAfterT()88 public void testRegisterComponentCallbacksWithoutBaseContextAfterT() { 89 final ContextWrapper wrapper = new TestContextWrapper(null /* base */); 90 final ComponentCallbacks callbacks = new TestComponentCallbacks2(); 91 92 try { 93 wrapper.unregisterComponentCallbacks(callbacks); 94 fail("ContextWrapper#unregisterComponentCallbacks must throw Exception before" 95 + " ContextWrapper#attachToBaseContext."); 96 } catch (IllegalStateException ignored) { 97 // It is expected to throw IllegalStateException. 98 } 99 100 try { 101 wrapper.registerComponentCallbacks(callbacks); 102 fail("ContextWrapper#registerComponentCallbacks must throw Exception before" 103 + " ContextWrapper#attachToBaseContext."); 104 } catch (IllegalStateException ignored) { 105 // It is expected to throw IllegalStateException. 106 } 107 } 108 109 /** 110 * {@link ContextWrapper#registerComponentCallbacks(ComponentCallbacks)} must delegate to its 111 * {@link ContextWrapper#getBaseContext()}, so does 112 * {@link ContextWrapper#unregisterComponentCallbacks(ComponentCallbacks)}. 113 */ 114 @Test testRegisterComponentCallbacks()115 public void testRegisterComponentCallbacks() { 116 final Context appContext = ApplicationProvider.getApplicationContext(); 117 final Display display = appContext.getSystemService(DisplayManager.class) 118 .getDisplay(DEFAULT_DISPLAY); 119 final WindowContext windowContext = (WindowContext) appContext.createWindowContext(display, 120 TYPE_APPLICATION_OVERLAY, null /* options */); 121 final ContextWrapper wrapper = new ContextWrapper(windowContext); 122 final TestComponentCallbacks2 callbacks = new TestComponentCallbacks2(); 123 124 wrapper.registerComponentCallbacks(callbacks); 125 126 assertThat(wrapper.mCallbacksRegisteredToSuper).isNull(); 127 128 final Configuration dispatchedConfig = new Configuration(); 129 dispatchedConfig.fontScale = 1.2f; 130 dispatchedConfig.windowConfiguration.setWindowingMode( 131 WindowConfiguration.WINDOWING_MODE_FREEFORM); 132 dispatchedConfig.windowConfiguration.setBounds(new Rect(0, 0, 100, 100)); 133 windowContext.dispatchConfigurationChanged(dispatchedConfig); 134 135 assertThat(callbacks.mConfiguration).isEqualTo(dispatchedConfig); 136 } 137 138 private static class TestContextWrapper extends ContextWrapper { TestContextWrapper(Context base)139 TestContextWrapper(Context base) { 140 super(base); 141 } 142 143 @Override getApplicationContext()144 public Context getApplicationContext() { 145 // The default implementation of ContextWrapper#getApplicationContext is to delegate 146 // to the base Context, and it leads to NPE if #registerComponentCallbacks is called 147 // directly before attach to base Context. 148 // We call to ApplicationProvider#getApplicationContext to prevent NPE because 149 // developers may have its implementation to prevent NPE without attaching base Context. 150 final Context baseContext = getBaseContext(); 151 if (baseContext == null) { 152 return ApplicationProvider.getApplicationContext(); 153 } else { 154 return super.getApplicationContext(); 155 } 156 } 157 } 158 } 159