/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.window; import static android.view.Display.DEFAULT_DISPLAY; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.annotation.UiContext; import android.app.ActivityThread; import android.app.LoadedApk; import android.app.Service; import android.content.ComponentCallbacks; import android.content.ComponentCallbacksController; import android.content.Context; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.IBinder; import android.view.Display; import android.view.WindowManager; import android.view.WindowManager.LayoutParams.WindowType; import android.view.WindowManagerImpl; /** * A {@link Service} responsible for showing a non-activity window, such as software keyboards or * accessibility overlay windows. This {@link Service} has similar behavior to * {@link WindowContext}, but is represented as {@link Service}. * * @see android.inputmethodservice.InputMethodService * * @hide */ @TestApi @UiContext public abstract class WindowProviderService extends Service implements WindowProvider { private final Bundle mOptions; private final WindowTokenClient mWindowToken = new WindowTokenClient(); private final WindowContextController mController = new WindowContextController(mWindowToken); private WindowManager mWindowManager; private boolean mInitialized; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); /** * Returns {@code true} if the {@code windowContextOptions} declares that it is a * {@link WindowProviderService}. * * @hide */ public static boolean isWindowProviderService(@Nullable Bundle windowContextOptions) { if (windowContextOptions == null) { return false; } return (windowContextOptions.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false)); } public WindowProviderService() { mOptions = new Bundle(); mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true); } /** * Returns the window type of this {@link WindowProviderService}. * Each inheriting class must implement this method to provide the type of the window. It is * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)} * * @see Context#createWindowContext(int, Bundle) * * @hide */ @TestApi @SuppressLint("OnNameExpected") // Suppress the lint because it is not a callback and users should provide window type // so we cannot make it final. @WindowType @Override public abstract int getWindowType(); /** * Returns the option of this {@link WindowProviderService}. *
* The inheriting class can implement this method to provide the customization {@code option} of * the window, but must be based on this method's returned value. * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)} *
*
* public Bundle getWindowContextOptions() {
* final Bundle options = super.getWindowContextOptions();
* options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
* return options;
* }
*
*
* @hide
*/
@TestApi
@SuppressLint({"OnNameExpected", "NullableCollection"})
// Suppress the lint because it is not a callback and users may override this API to provide
// launch option. Also, the return value of this API is null by default.
@Nullable
@CallSuper
@Override
public Bundle getWindowContextOptions() {
return mOptions;
}
@SuppressLint({"OnNameExpected", "ExecutorRegistration"})
// Suppress lint because this is a legacy named function and doesn't have an optional param
// for executor.
// TODO(b/259347943): Update documentation for U.
/**
* Here we override to prevent WindowProviderService from invoking
* {@link Application.registerComponentCallback}, which will result in callback registered
* for process-level Configuration change updates.
*/
@Override
public void registerComponentCallbacks(@NonNull ComponentCallbacks callback) {
// For broadcasting Configuration Changes.
mCallbacksController.registerCallbacks(callback);
}
@SuppressLint("OnNameExpected")
@Override
public void unregisterComponentCallbacks(@NonNull ComponentCallbacks callback) {
mCallbacksController.unregisterCallbacks(callback);
}
@SuppressLint("OnNameExpected")
@Override
public void onConfigurationChanged(@NonNull Configuration configuration) {
// This is only called from WindowTokenClient.
mCallbacksController.dispatchConfigurationChanged(configuration);
}
/**
* Override {@link Service}'s empty implementation and listen to {@link ActivityThread} for
* low memory and trim memory events.
*/
@Override
public void onLowMemory() {
mCallbacksController.dispatchLowMemory();
}
@Override
public void onTrimMemory(int level) {
mCallbacksController.dispatchTrimMemory(level);
}
/**
* Returns the display ID to launch this {@link WindowProviderService}.
*
* @hide
*/
@TestApi
@SuppressLint({"OnNameExpected"})
// Suppress the lint because it is not a callback and users may override this API to provide
// display.
@NonNull
public int getInitialDisplayId() {
return DEFAULT_DISPLAY;
}
/**
* Attaches this WindowProviderService to the {@code windowToken}.
*
* @hide
*/
@TestApi
public final void attachToWindowToken(@NonNull IBinder windowToken) {
mController.attachToWindowToken(windowToken);
}
/** @hide */
@Override
public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo);
final Display display = context.getSystemService(DisplayManager.class)
.getDisplay(getInitialDisplayId());
return context.createTokenContext(mWindowToken, display);
}
/** @hide */
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
if (!mInitialized) {
mWindowToken.attachContext(this);
mController.attachToDisplayArea(getWindowType(), getDisplayId(),
getWindowContextOptions());
mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
mInitialized = true;
}
}
// Suppress the lint because ths is overridden from Context.
@SuppressLint("OnNameExpected")
@Override
@Nullable
public Object getSystemService(@NonNull String name) {
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
}
return super.getSystemService(name);
}
@CallSuper
@Override
public void onDestroy() {
super.onDestroy();
mController.detachIfNeeded();
mCallbacksController.clearCallbacks();
}
}