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

Commit e2d71f89 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Extract multi-instance checks into separate helper" into main

parents 767e6dfd 578f4ebc
Loading
Loading
Loading
Loading
+124 −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 com.android.wm.shell.common

import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.os.UserHandle
import android.view.WindowManager
import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.R
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
import com.android.wm.shell.util.KtProtoLog
import java.util.Arrays

/**
 * Helper for multi-instance related checks.
 */
class MultiInstanceHelper @JvmOverloads constructor(
    private val context: Context,
    private val packageManager: PackageManager,
    private val staticAppsSupportingMultiInstance: Array<String> = context.resources
            .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) {

    /**
     * Returns whether a specific component desires to be launched in multiple instances.
     */
    @VisibleForTesting
    fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
        if (componentName == null || componentName.packageName == null) {
            // TODO(b/262864589): Handle empty component case
            return false
        }

        // Check the pre-defined allow list
        val packageName = componentName.packageName
        for (pkg in staticAppsSupportingMultiInstance) {
            if (pkg == packageName) {
                KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance",
                    packageName)
                return true
            }
        }

        // Check the activity property first
        try {
            val activityProp = packageManager.getProperty(
                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
            // If the above call doesn't throw a NameNotFoundException, then the activity property
            // should override the application property value
            if (activityProp.isBoolean) {
                KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName)
                return activityProp.boolean
            } else {
                KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d",
                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type)
            }
        } catch (nnfe: PackageManager.NameNotFoundException) {
            // Not specified in the activity, fall through
        }

        // Check the application property otherwise
        try {
            val appProp = packageManager.getProperty(
                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
            if (appProp.isBoolean) {
                KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
                return appProp.boolean
            } else {
                KtProtoLog.w(WM_SHELL,
                    "Warning: property=%s for application=%s has non-bool type=%d",
                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type)
            }
        } catch (nnfe: PackageManager.NameNotFoundException) {
            // Not specified in either application or activity
        }
        return false
    }

    companion object {
        /** Returns the component from a PendingIntent  */
        @JvmStatic
        fun getComponent(pendingIntent: PendingIntent?): ComponentName? {
            return pendingIntent?.intent?.component
        }

        /** Returns the component from a shortcut  */
        @JvmStatic
        fun getShortcutComponent(packageName: String, shortcutId: String,
                user: UserHandle, launcherApps: LauncherApps): ComponentName? {
            val query = LauncherApps.ShortcutQuery()
            query.setPackage(packageName)
            query.setShortcutIds(Arrays.asList(shortcutId))
            query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED)
            val shortcuts = launcherApps.getShortcuts(query, user)
            val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null
            return info?.activity
        }

        /** Returns true if package names and user ids match.  */
        @JvmStatic
        fun samePackage(packageName1: String?, packageName2: String?,
                userId1: Int, userId2: Int): Boolean {
            return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2)
        }
    }
}
+0 −30
Original line number Diff line number Diff line
@@ -99,12 +99,6 @@ public class SplitScreenUtils {
        return taskInfo != null ? taskInfo.userId : -1;
    }

    /** Returns true if package names and user ids match. */
    public static boolean samePackage(String packageName1, String packageName2,
            int userId1, int userId2) {
        return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2);
    }

    /** Generates a common log message for split screen failures */
    public static String splitFailureMessage(String caller, String reason) {
        return "(" + caller + ") Splitscreen aborted: " + reason;
@@ -143,28 +137,4 @@ public class SplitScreenUtils {
            return isLandscape;
        }
    }

    /** Returns the component from a PendingIntent */
    @Nullable
    public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) {
        if (pendingIntent == null || pendingIntent.getIntent() == null) {
            return null;
        }
        return pendingIntent.getIntent().getComponent();
    }

    /** Returns the component from a shortcut */
    @Nullable
    public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId,
            @NonNull UserHandle user, @NonNull LauncherApps launcherApps) {
        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
        query.setPackage(packageName);
        query.setShortcutIds(Arrays.asList(shortcutId));
        query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
        List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user);
        ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
                ? shortcuts.get(0)
                : null;
        return info != null ? info.getActivity() : null;
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -88,13 +89,14 @@ public class TvWMShellModule {
            IconProvider iconProvider,
            Optional<RecentTasksController> recentTasks,
            LaunchAdjacentController launchAdjacentController,
            MultiInstanceHelper multiInstanceHelper,
            @ShellMainThread ShellExecutor mainExecutor,
            Handler mainHandler,
            SystemWindows systemWindows) {
        return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
                displayImeController, displayInsetsController, transitions, transactionPool,
                iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
                systemWindows);
                iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
                mainExecutor, mainHandler, systemWindows);
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -322,6 +323,12 @@ public abstract class WMShellBaseModule {
        return Optional.of(perfHintController.getHinter());
    }

    @WMSingleton
    @Provides
    static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
        return new MultiInstanceHelper(context, context.getPackageManager());
    }

    //
    // Back animation
    //
+4 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -347,12 +348,14 @@ public abstract class WMShellModule {
            LaunchAdjacentController launchAdjacentController,
            Optional<WindowDecorViewModel> windowDecorViewModel,
            Optional<DesktopTasksController> desktopTasksController,
            MultiInstanceHelper multiInstanceHelper,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
                shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
                displayImeController, displayInsetsController, dragAndDropController, transitions,
                transactionPool, iconProvider, recentTasks, launchAdjacentController,
                windowDecorViewModel, desktopTasksController, mainExecutor);
                windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
                multiInstanceHelper, mainExecutor);
    }

    //
Loading