1 /* 2 * Copyright (C) 2021 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.window; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 21 import android.annotation.CallSuper; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.annotation.TestApi; 26 import android.annotation.UiContext; 27 import android.app.ActivityThread; 28 import android.app.LoadedApk; 29 import android.app.Service; 30 import android.content.ComponentCallbacks; 31 import android.content.ComponentCallbacksController; 32 import android.content.Context; 33 import android.content.res.Configuration; 34 import android.hardware.display.DisplayManager; 35 import android.os.Bundle; 36 import android.os.IBinder; 37 import android.util.Log; 38 import android.view.Display; 39 import android.view.WindowManager; 40 import android.view.WindowManager.LayoutParams.WindowType; 41 import android.view.WindowManagerImpl; 42 43 /** 44 * A {@link Service} responsible for showing a non-activity window, such as software keyboards or 45 * accessibility overlay windows. This {@link Service} has similar behavior to 46 * {@link WindowContext}, but is represented as {@link Service}. 47 * 48 * @see android.inputmethodservice.InputMethodService 49 * 50 * @hide 51 */ 52 @TestApi 53 @UiContext 54 public abstract class WindowProviderService extends Service implements WindowProvider { 55 56 private static final String TAG = WindowProviderService.class.getSimpleName(); 57 58 private final Bundle mOptions; 59 private final WindowTokenClient mWindowToken = new WindowTokenClient(); 60 private final WindowContextController mController = new WindowContextController(mWindowToken); 61 private WindowManager mWindowManager; 62 private boolean mInitialized; 63 private final ComponentCallbacksController mCallbacksController = 64 new ComponentCallbacksController(); 65 66 /** 67 * Returns {@code true} if the {@code windowContextOptions} declares that it is a 68 * {@link WindowProviderService}. 69 * 70 * @hide 71 */ isWindowProviderService(@ullable Bundle windowContextOptions)72 public static boolean isWindowProviderService(@Nullable Bundle windowContextOptions) { 73 if (windowContextOptions == null) { 74 return false; 75 } 76 return (windowContextOptions.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false)); 77 } 78 WindowProviderService()79 public WindowProviderService() { 80 mOptions = new Bundle(); 81 mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true); 82 } 83 84 /** 85 * Returns the window type of this {@link WindowProviderService}. 86 * Each inheriting class must implement this method to provide the type of the window. It is 87 * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)} 88 * 89 * @see Context#createWindowContext(int, Bundle) 90 * 91 * @hide 92 */ 93 @TestApi 94 @SuppressLint("OnNameExpected") 95 // Suppress the lint because it is not a callback and users should provide window type 96 // so we cannot make it final. 97 @WindowType 98 @Override getWindowType()99 public abstract int getWindowType(); 100 101 /** 102 * Returns the option of this {@link WindowProviderService}. 103 * <p> 104 * The inheriting class can implement this method to provide the customization {@code option} of 105 * the window, but must be based on this method's returned value. 106 * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)} 107 * </p> 108 * <pre class="prettyprint"> 109 * public Bundle getWindowContextOptions() { 110 * final Bundle options = super.getWindowContextOptions(); 111 * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId); 112 * return options; 113 * } 114 * </pre> 115 * 116 * @hide 117 */ 118 @TestApi 119 @SuppressLint({"OnNameExpected", "NullableCollection"}) 120 // Suppress the lint because it is not a callback and users may override this API to provide 121 // launch option. Also, the return value of this API is null by default. 122 @Nullable 123 @CallSuper 124 @Override getWindowContextOptions()125 public Bundle getWindowContextOptions() { 126 return mOptions; 127 } 128 129 @SuppressLint({"OnNameExpected", "ExecutorRegistration"}) 130 // Suppress lint because this is a legacy named function and doesn't have an optional param 131 // for executor. 132 // TODO(b/259347943): Update documentation for U. 133 /** 134 * Here we override to prevent WindowProviderService from invoking 135 * {@link Application.registerComponentCallback}, which will result in callback registered 136 * for process-level Configuration change updates. 137 */ 138 @Override registerComponentCallbacks(@onNull ComponentCallbacks callback)139 public void registerComponentCallbacks(@NonNull ComponentCallbacks callback) { 140 // For broadcasting Configuration Changes. 141 mCallbacksController.registerCallbacks(callback); 142 } 143 144 @SuppressLint("OnNameExpected") 145 @Override unregisterComponentCallbacks(@onNull ComponentCallbacks callback)146 public void unregisterComponentCallbacks(@NonNull ComponentCallbacks callback) { 147 mCallbacksController.unregisterCallbacks(callback); 148 } 149 150 @SuppressLint("OnNameExpected") 151 @Override onConfigurationChanged(@onNull Configuration configuration)152 public void onConfigurationChanged(@NonNull Configuration configuration) { 153 // This is only called from WindowTokenClient. 154 mCallbacksController.dispatchConfigurationChanged(configuration); 155 } 156 157 /** 158 * Override {@link Service}'s empty implementation and listen to {@link ActivityThread} for 159 * low memory and trim memory events. 160 */ 161 @Override onLowMemory()162 public void onLowMemory() { 163 mCallbacksController.dispatchLowMemory(); 164 } 165 166 @Override onTrimMemory(int level)167 public void onTrimMemory(int level) { 168 mCallbacksController.dispatchTrimMemory(level); 169 } 170 171 /** 172 * Returns the display ID to launch this {@link WindowProviderService}. 173 * 174 * @hide 175 */ 176 @TestApi 177 @SuppressLint({"OnNameExpected"}) 178 // Suppress the lint because it is not a callback and users may override this API to provide 179 // display. 180 @NonNull getInitialDisplayId()181 public int getInitialDisplayId() { 182 return DEFAULT_DISPLAY; 183 } 184 185 /** 186 * Attaches this WindowProviderService to the {@code windowToken}. 187 * 188 * @hide 189 */ 190 @TestApi attachToWindowToken(@onNull IBinder windowToken)191 public final void attachToWindowToken(@NonNull IBinder windowToken) { 192 mController.attachToWindowToken(windowToken); 193 } 194 195 /** @hide */ 196 @Override createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo)197 public final Context createServiceBaseContext(ActivityThread mainThread, 198 LoadedApk packageInfo) { 199 final Context context = super.createServiceBaseContext(mainThread, packageInfo); 200 final DisplayManager displayManager = context.getSystemService(DisplayManager.class); 201 final int initialDisplayId = getInitialDisplayId(); 202 Display display = displayManager.getDisplay(initialDisplayId); 203 // Fallback to use the default display if the initial display to start WindowProviderService 204 // is detached. 205 if (display == null) { 206 Log.e(TAG, "Display with id " + initialDisplayId + " not found, falling back to " 207 + "DEFAULT_DISPLAY"); 208 display = displayManager.getDisplay(DEFAULT_DISPLAY); 209 } 210 return context.createTokenContext(mWindowToken, display); 211 } 212 213 /** @hide */ 214 @Override attachBaseContext(Context newBase)215 protected void attachBaseContext(Context newBase) { 216 super.attachBaseContext(newBase); 217 if (!mInitialized) { 218 mWindowToken.attachContext(this); 219 mController.attachToDisplayArea(getWindowType(), getDisplayId(), 220 getWindowContextOptions()); 221 mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this); 222 mInitialized = true; 223 } 224 } 225 226 // Suppress the lint because ths is overridden from Context. 227 @SuppressLint("OnNameExpected") 228 @Override 229 @Nullable getSystemService(@onNull String name)230 public Object getSystemService(@NonNull String name) { 231 if (WINDOW_SERVICE.equals(name)) { 232 return mWindowManager; 233 } 234 return super.getSystemService(name); 235 } 236 237 @CallSuper 238 @Override onDestroy()239 public void onDestroy() { 240 super.onDestroy(); 241 mController.detachIfNeeded(); 242 mCallbacksController.clearCallbacks(); 243 } 244 } 245