Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49cd50b3 authored by Charles Chen's avatar Charles Chen
Browse files

Introduce WindowProviderService

A Window Provider Service is a Window-Context-like Service which handles
UI
components and is able to obtain the latest configuration.
The differences between a Window Context and a Window Provider Service
is that:
1. It is always associated with the primary display before
   attachWindowToken() or WM#addView is called. It is suggested to
render UI
   components after calling the APIs mentioned above.
2. A window context registers the listener in constructor and
   unregisters it in finalize(), while a window provider service
   registers the listener in onCreate() and unregisters in onDestroy().
3. Like the API Context#createWindowContext(int windowType, Bundle
options),
   the users of a Window Provider Service need to override
provideWindowType and
   provideOptions to pass the attributes.
4. When there's a configuration updates from the server side,
   the Service#onConfigurationChanged callback will be invoked.(TBD)
It is suggested to use window context when possible. This class is to
migrate the
Service to show UI components to the window context concept. We can't
migrate them
to WindowContext directly because developers are used to use this kind
of Service as
the container of UI components and may change its property at runtime.
An example is that keyboard developers may apply a new theme by
InputMethodService#getResources#setTheme(newTheme).

Bug: 159767464
Test: atest WindowContextTests#testWindowProviderServiceLifecycle

Change-Id: I7d537fd2d128efa28aa6e771d77aa105fb497672
parent a5b660e3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -3163,5 +3163,12 @@ package android.window {
    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
  }

  @UiContext public abstract class WindowProviderService extends android.app.Service {
    ctor public WindowProviderService();
    method public final void attachToWindowToken(@NonNull android.os.IBinder);
    method @Nullable public android.os.Bundle getWindowContextOptions();
    method public abstract int getWindowType();
  }

}
+2 −1
Original line number Diff line number Diff line
@@ -4385,11 +4385,12 @@ public final class ActivityThread extends ClientTransactionHandler
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            final ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));
            // Service resources must be initialized with the same loaders as the application
            // context.
            context.getResources().addLoaders(
+13 −0
Original line number Diff line number Diff line
@@ -860,6 +860,19 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
        setContentCaptureOptions(application.getContentCaptureOptions());
    }

    /**
     * Creates the base {@link Context} of this {@link Service}.
     * Users may override this API to create customized base context.
     *
     * @see android.window.WindowProviderService WindowProviderService class for example
     * @see ContextWrapper#attachBaseContext(Context)
     *
     * @hide
     */
    public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return ContextImpl.createAppContext(mainThread, packageInfo);
    }

    /**
     * @hide
     * Clean up any references to avoid leaks.
+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ public class WindowContextController {
     * a {@link com.android.server.wm.DisplayArea} by
     * {@link #attachToDisplayArea(int, int, Bundle)}.
     *
     * @see WindowProviderService#attachToWindowToken(IBinder))
     * @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder)
     */
    public void attachToWindowToken(IBinder windowToken) {
+138 −0
Original line number Diff line number Diff line
/*
 * 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.Context;
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;

// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
/**
 * 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
 * @see android.accessibilityservice.AccessibilityService
 *
 * @hide
 */
@TestApi
@UiContext
public abstract class WindowProviderService extends Service {

    private final WindowTokenClient mWindowToken = new WindowTokenClient();
    private final WindowContextController mController = new WindowContextController(mWindowToken);
    private WindowManager mWindowManager;

    /**
     * Returns the 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.
    public abstract @WindowType int getWindowType();

    /**
     * Returns the option of this {@link WindowProviderService}.
     * Default is {@code null}. The inheriting class can implement this method to provide the
     * customization {@code option} of the window. It is used similar to {@code options} of
     * {@link Context#createWindowContext(int, Bundle)}
     *
     * @see Context#createWindowContext(int, Bundle)
     *
     * @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
    public Bundle getWindowContextOptions() {
        return null;
    }

    /**
     * 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);
        // Always associate with the default display at initialization.
        final Display defaultDisplay = context.getSystemService(DisplayManager.class)
                .getDisplay(DEFAULT_DISPLAY);
        return context.createTokenContext(mWindowToken, defaultDisplay);
    }

    @CallSuper
    @Override
    public void onCreate() {
        super.onCreate();
        mWindowToken.attachContext(this);
        mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions());
        mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
    }

    @SuppressLint("OnNameExpected")
    @Override
    // Suppress the lint because ths is overridden from Context.
    public @Nullable 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();
    }
}