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

Commit 1ff72183 authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Reland WM Extensions for all devices" into main

parents 2040daa6 8128127f
Loading
Loading
Loading
Loading
+57 −5
Original line number Diff line number Diff line
@@ -93,8 +93,12 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.KeyguardManager;
import android.app.Presentation;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ComponentName;
@@ -1416,23 +1420,71 @@ public interface WindowManager extends ViewManager {
    public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";

    /**
     * Whether the device supports the WindowManager Extensions.
     * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
     * Whether the WindowManager Extensions - Activity Embedding feature should be guarded by
     * the app's target SDK on Android 15.
     *
     * WindowManager Extensions are only required for foldable and large screen before Android 15,
     * so we want to guard the Activity Embedding feature since it can have app compat impact on
     * devices with a compact size display.
     *
     * <p>If {@code true}, the feature is only enabled if the app's target SDK is Android 15 or
     * above.
     *
     * <p>If {@code false}, the feature is enabled for all apps.
     *
     * <p>The default value is {@code true}. OEMs can set to {@code false} by having their device
     * config to inherit window_extensions.mk. This is also required for large screen devices.
     * <pre>
     * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
     * </pre>
     *
     * @hide
     */
    boolean ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15 = SystemProperties.getBoolean(
            "persist.wm.extensions.activity_embedding_guard_with_android_15", true);

    /**
     * For devices with {@link #ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15} as {@code true},
     * the Activity Embedding feature is enabled if the app's target SDK is Android 15+.
     *
     * @see #ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15
     * @hide
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
    long ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15 = 306666082L;

    /**
     * Whether the device contains the WindowManager Extensions shared library.
     * This is enabled for all devices through window_extensions_base.mk, but can be dropped if the
     * device doesn't support multi window.
     *
     * <p>Note: Large screen devices must also inherit window_extensions.mk to enable the Activity
     * Embedding feature by default for all apps.
     *
     * @see #ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15
     * @hide
     */
    boolean WINDOW_EXTENSIONS_ENABLED =
    boolean HAS_WINDOW_EXTENSIONS_ON_DEVICE =
            SystemProperties.getBoolean("persist.wm.extensions.enabled", false);

    /**
     * @see #WINDOW_EXTENSIONS_ENABLED
     * Whether the WindowManager Extensions are enabled.
     * If {@code false}, the WM Jetpack will report most of its features as disabled.
     * @see #HAS_WINDOW_EXTENSIONS_ON_DEVICE
     * @hide
     */
    @TestApi
    static boolean hasWindowExtensionsEnabled() {
        return WINDOW_EXTENSIONS_ENABLED;
        return HAS_WINDOW_EXTENSIONS_ON_DEVICE
                && ActivityTaskManager.supportsMultiWindow(ActivityThread.currentApplication())
                // Since enableWmExtensionsForAllFlag, HAS_WINDOW_EXTENSIONS_ON_DEVICE is now true
                // on all devices by default as a build file property.
                // Until finishing flag ramp up, only return true when
                // ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15 is false, which is set per device by
                // OEMs.
                && (Flags.enableWmExtensionsForAllFlag()
                || !ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15);
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -400,7 +400,7 @@ public class ZygoteInit {
        // WindowManager Extensions is an optional shared library that is required for WindowManager
        // Jetpack to fully function. Since it is a widely used library, preload it to improve apps
        // startup performance.
        if (WindowManager.hasWindowExtensionsEnabled()) {
        if (WindowManager.HAS_WINDOW_EXTENSIONS_ON_DEVICE) {
            final String systemExtFrameworkPath =
                    new File(Environment.getSystemExtDirectory(), "framework").getPath();
            libs.add(new SharedLibraryInfo(
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.view;

import static com.android.window.flags.Flags.FLAG_ENABLE_WM_EXTENSIONS_FOR_ALL_FLAG;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests for the {@link WindowManager}.
 *
 * Build/Install/Run:
 *  atest FrameworksCoreTests:WindowManagerTests
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class WindowManagerTests {

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Test
    public void testHasWindowExtensionsEnabled_flagDisabled() {
        mSetFlagsRule.disableFlags(FLAG_ENABLE_WM_EXTENSIONS_FOR_ALL_FLAG);

        // Before FLAG_ENABLE_WM_EXTENSIONS_FOR_ALL_FLAG, Extensions are always bundled with AE.
        assertEquals(isActivityEmbeddingEnableForAll(),
                WindowManager.hasWindowExtensionsEnabled());
    }

    @Test
    public void testHasWindowExtensionsEnabled_flagEnabled() {
        mSetFlagsRule.enableFlags(FLAG_ENABLE_WM_EXTENSIONS_FOR_ALL_FLAG);

        // Extensions should be enabled on all devices.
        assertTrue(WindowManager.hasWindowExtensionsEnabled());
    }

    @Test
    public void testActivityEmbeddingAvailability() {
        assumeTrue(isActivityEmbeddingEnableForAll());

        // AE can only be enabled when extensions is enabled.
        assertTrue(WindowManager.hasWindowExtensionsEnabled());
    }

    private static boolean isActivityEmbeddingEnableForAll() {
        return !WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15;
    }
}
+63 −26
Original line number Diff line number Diff line
@@ -16,15 +16,19 @@

package androidx.window.extensions;

import android.app.ActivityTaskManager;
import static android.view.WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15;
import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15;

import android.app.ActivityThread;
import android.app.Application;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.extensions.area.WindowAreaComponent;
@@ -38,25 +42,38 @@ import java.util.Objects;


/**
 * The reference implementation of {@link WindowExtensions} that implements the initial API version.
 * The reference implementation of {@link WindowExtensions} that implements the latest WindowManager
 * Extensions APIs.
 */
public class WindowExtensionsImpl implements WindowExtensions {
class WindowExtensionsImpl implements WindowExtensions {

    private static final String TAG = "WindowExtensionsImpl";

    /**
     * The min version of the WM Extensions that must be supported in the current platform version.
     */
    @VisibleForTesting
    static final int EXTENSIONS_VERSION_CURRENT_PLATFORM = 5;

    private final Object mLock = new Object();
    private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
    private volatile WindowLayoutComponentImpl mWindowLayoutComponent;
    private volatile SplitController mSplitController;
    private volatile WindowAreaComponent mWindowAreaComponent;

    public WindowExtensionsImpl() {
        Log.i(TAG, "Initializing Window Extensions.");
    private final int mVersion = EXTENSIONS_VERSION_CURRENT_PLATFORM;
    private final boolean mIsActivityEmbeddingEnabled;

    WindowExtensionsImpl() {
        mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled();
        Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion
                + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled);
    }

    // TODO(b/241126279) Introduce constants to better version functionality
    @Override
    public int getVendorApiLevel() {
        return 5;
        return mVersion;
    }

    @NonNull
@@ -74,8 +91,8 @@ public class WindowExtensionsImpl implements WindowExtensions {
        if (mFoldingFeatureProducer == null) {
            synchronized (mLock) {
                if (mFoldingFeatureProducer == null) {
                    Context context = getApplication();
                    RawFoldingFeatureProducer foldingFeatureProducer =
                    final Context context = getApplication();
                    final RawFoldingFeatureProducer foldingFeatureProducer =
                            new RawFoldingFeatureProducer(context);
                    mFoldingFeatureProducer =
                            new DeviceStateManagerFoldingFeatureProducer(context,
@@ -91,8 +108,8 @@ public class WindowExtensionsImpl implements WindowExtensions {
        if (mWindowLayoutComponent == null) {
            synchronized (mLock) {
                if (mWindowLayoutComponent == null) {
                    Context context = getApplication();
                    DeviceStateManagerFoldingFeatureProducer producer =
                    final Context context = getApplication();
                    final DeviceStateManagerFoldingFeatureProducer producer =
                            getFoldingFeatureProducer();
                    mWindowLayoutComponent = new WindowLayoutComponentImpl(context, producer);
                }
@@ -102,29 +119,35 @@ public class WindowExtensionsImpl implements WindowExtensions {
    }

    /**
     * Returns a reference implementation of {@link WindowLayoutComponent} if available,
     * {@code null} otherwise. The implementation must match the API level reported in
     * {@link WindowExtensions#getWindowLayoutComponent()}.
     * Returns a reference implementation of the latest {@link WindowLayoutComponent}.
     *
     * The implementation must match the API level reported in
     * {@link WindowExtensions#getVendorApiLevel()}.
     *
     * @return {@link WindowLayoutComponent} OEM implementation
     */
    @NonNull
    @Override
    public WindowLayoutComponent getWindowLayoutComponent() {
        return getWindowLayoutComponentImpl();
    }

    /**
     * Returns a reference implementation of {@link ActivityEmbeddingComponent} if available,
     * {@code null} otherwise. The implementation must match the API level reported in
     * {@link WindowExtensions#getWindowLayoutComponent()}.
     * Returns a reference implementation of the latest {@link ActivityEmbeddingComponent} if the
     * device supports this feature, {@code null} otherwise.
     *
     * The implementation must match the API level reported in
     * {@link WindowExtensions#getVendorApiLevel()}.
     *
     * @return {@link ActivityEmbeddingComponent} OEM implementation.
     */
    @Nullable
    @Override
    public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
        if (mSplitController == null) {
            if (!ActivityTaskManager.supportsMultiWindow(getApplication())) {
                // Disable AE for device that doesn't support multi window.
        if (!mIsActivityEmbeddingEnabled) {
            return null;
        }
        if (mSplitController == null) {
            synchronized (mLock) {
                if (mSplitController == null) {
                    mSplitController = new SplitController(
@@ -138,21 +161,35 @@ public class WindowExtensionsImpl implements WindowExtensions {
    }

    /**
     * Returns a reference implementation of {@link WindowAreaComponent} if available,
     * {@code null} otherwise. The implementation must match the API level reported in
     * {@link WindowExtensions#getWindowAreaComponent()}.
     * Returns a reference implementation of the latest {@link WindowAreaComponent}
     *
     * The implementation must match the API level reported in
     * {@link WindowExtensions#getVendorApiLevel()}.
     *
     * @return {@link WindowAreaComponent} OEM implementation.
     */
    @Nullable
    @Override
    public WindowAreaComponent getWindowAreaComponent() {
        if (mWindowAreaComponent == null) {
            synchronized (mLock) {
                if (mWindowAreaComponent == null) {
                    Context context = ActivityThread.currentApplication();
                    mWindowAreaComponent =
                            new WindowAreaComponentImpl(context);
                    final Context context = getApplication();
                    mWindowAreaComponent = new WindowAreaComponentImpl(context);
                }
            }
        }
        return mWindowAreaComponent;
    }

    @VisibleForTesting
    static boolean isActivityEmbeddingEnabled() {
        if (!ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15) {
            // Device enables it for all apps without targetSDK check.
            // This must be true for all large screen devices.
            return true;
        }
        // Use compat framework to guard the feature with targetSDK 15.
        return CompatChanges.isChangeEnabled(ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15);
    }
}
+46 −2
Original line number Diff line number Diff line
@@ -16,14 +16,20 @@

package androidx.window.extensions;

import android.annotation.NonNull;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.extensions.area.WindowAreaComponent;
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
import androidx.window.extensions.layout.WindowLayoutComponent;

/**
 * Provides the OEM implementation of {@link WindowExtensions}.
 */
public class WindowExtensionsProvider {

    private static final WindowExtensions sWindowExtensions = new WindowExtensionsImpl();
    private static volatile WindowExtensions sWindowExtensions;

    /**
     * Returns the OEM implementation of {@link WindowExtensions}. This method is implemented in
@@ -33,6 +39,44 @@ public class WindowExtensionsProvider {
     */
    @NonNull
    public static WindowExtensions getWindowExtensions() {
        if (sWindowExtensions == null) {
            synchronized (WindowExtensionsProvider.class) {
                if (sWindowExtensions == null) {
                    sWindowExtensions = WindowManager.hasWindowExtensionsEnabled()
                            ? new WindowExtensionsImpl()
                            : new DisabledWindowExtensions();
                }
            }
        }
        return sWindowExtensions;
    }

    /**
     * The stub version to return when the WindowManager Extensions is disabled
     * @see WindowManager#hasWindowExtensionsEnabled
     */
    private static class DisabledWindowExtensions implements WindowExtensions {
        @Override
        public int getVendorApiLevel() {
            return 0;
        }

        @Nullable
        @Override
        public WindowLayoutComponent getWindowLayoutComponent() {
            return null;
        }

        @Nullable
        @Override
        public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
            return null;
        }

        @Nullable
        @Override
        public WindowAreaComponent getWindowAreaComponent() {
            return null;
        }
    }
}
Loading