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

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

Merge "Pass multi-instance state via LauncherActivityInfo" into main

parents e80f1b9a f45aac8e
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -212,6 +212,14 @@ public class LauncherActivityInfo {
        return mPm.getUserBadgedIcon(originalIcon, mInternal.getUser());
    }

    /**
     * Returns whether this activity supports (and can be launched in) multiple instances.
     * @hide
     */
    public boolean supportsMultiInstance() {
        return mInternal.supportsMultiInstance();
    }

    /**
     * Check whether the {@code sequence} is visible to the user or not.
     * <p>
+12 −1
Original line number Diff line number Diff line
@@ -32,19 +32,24 @@ public class LauncherActivityInfoInternal implements Parcelable {
    @NonNull private ComponentName mComponentName;
    @NonNull private IncrementalStatesInfo mIncrementalStatesInfo;
    @NonNull private UserHandle mUser;
    private boolean mSupportsMultiInstance;

    /**
     * @param info ActivityInfo from which to create the LauncherActivityInfo.
     * @param incrementalStatesInfo The package's states.
     * @param user The user the activity info belongs to.
     * @param supportsMultiInstance Whether the activity supports multi-instance as declared in its
     *                              app manifest
     */
    public LauncherActivityInfoInternal(@NonNull ActivityInfo info,
            @NonNull IncrementalStatesInfo incrementalStatesInfo,
            @NonNull UserHandle user) {
            @NonNull UserHandle user,
            boolean supportsMultiInstance) {
        mActivityInfo = info;
        mComponentName = new ComponentName(info.packageName, info.name);
        mIncrementalStatesInfo = incrementalStatesInfo;
        mUser = user;
        mSupportsMultiInstance = supportsMultiInstance;
    }

    public LauncherActivityInfoInternal(Parcel source) {
@@ -52,6 +57,7 @@ public class LauncherActivityInfoInternal implements Parcelable {
        mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
        mIncrementalStatesInfo = source.readTypedObject(IncrementalStatesInfo.CREATOR);
        mUser = source.readTypedObject(UserHandle.CREATOR);
        mSupportsMultiInstance = source.readBoolean();
    }

    public ComponentName getComponentName() {
@@ -70,6 +76,10 @@ public class LauncherActivityInfoInternal implements Parcelable {
        return mIncrementalStatesInfo;
    }

    public boolean supportsMultiInstance() {
        return mSupportsMultiInstance;
    }

    @Override
    public int describeContents() {
        return 0;
@@ -80,6 +90,7 @@ public class LauncherActivityInfoInternal implements Parcelable {
        dest.writeTypedObject(mActivityInfo, flags);
        dest.writeTypedObject(mIncrementalStatesInfo, flags);
        dest.writeTypedObject(mUser, flags);
        dest.writeBoolean(mSupportsMultiInstance);
    }

    public static final @android.annotation.NonNull Creator<LauncherActivityInfoInternal> CREATOR =
+35 −5
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static android.content.PermissionChecker.checkCallingOrSelfPermissionForP
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;

import static com.android.server.pm.PackageArchiver.isArchivingEnabled;

@@ -118,7 +119,6 @@ import android.window.IDumpCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -850,7 +850,9 @@ public class LauncherAppsService extends SystemService {
                    // package does not exist; should not happen
                    return null;
                }
                return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo, user);
                return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo, user,
                        supportsMultiInstance(mIPM, activityInfo.getComponentName(),
                                user.getIdentifier()));
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -941,7 +943,7 @@ public class LauncherAppsService extends SystemService {
                        archiveState.getActivityInfos();
                for (int j = 0; j < archiveActivityInfoList.size(); j++) {
                    launcherActivityList.add(
                            constructLauncherActivityInfoForArchivedApp(
                            constructLauncherActivityInfoForArchivedApp(mIPM,
                                    user, applicationInfo, archiveActivityInfoList.get(j)));
                }
            }
@@ -949,6 +951,7 @@ public class LauncherAppsService extends SystemService {
        }

        private static LauncherActivityInfoInternal constructLauncherActivityInfoForArchivedApp(
                IPackageManager pm,
                UserHandle user,
                ApplicationInfo applicationInfo,
                ArchiveState.ArchiveActivityInfo archiveActivityInfo) {
@@ -964,7 +967,9 @@ public class LauncherAppsService extends SystemService {
                    activityInfo,
                    new IncrementalStatesInfo(
                            false /* isLoading */, 0 /* progress */, 0 /* loadingCompletedTime */),
                    user);
                    user,
                    supportsMultiInstance(pm, activityInfo.getComponentName(),
                            user.getIdentifier()));
        }

        @NonNull
@@ -1025,7 +1030,9 @@ public class LauncherAppsService extends SystemService {
                    continue;
                }
                results.add(new LauncherActivityInfoInternal(ri.activityInfo,
                        incrementalStatesInfo, user));
                        incrementalStatesInfo, user,
                        supportsMultiInstance(mIPM, ri.activityInfo.getComponentName(),
                                user.getIdentifier())));
            }
            return results;
        }
@@ -1660,6 +1667,29 @@ public class LauncherAppsService extends SystemService {
            }
        }

        /**
         * Returns whether the specified activity info has the multi-instance property declared.
         */
        @VisibleForTesting
        static boolean supportsMultiInstance(@NonNull IPackageManager pm,
                @NonNull ComponentName component, int userId) {
            try {
                // Try to get the property for the component
                return pm.getPropertyAsUser(
                        PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component.getPackageName(),
                        component.getClassName(), userId).getBoolean();
            } catch (Exception e) {
                try {
                    // Fallback to the property for the app
                    return pm.getPropertyAsUser(
                            PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component.getPackageName(),
                            null, userId).getBoolean();
                } catch (Exception e2) {
                    return false;
                }
            }
        }

        @Override
        public @Nullable LauncherUserInfo getLauncherUserInfo(@NonNull UserHandle user) {
            if (!canAccessProfile(user.getIdentifier(),
+123 −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.server.pm

import android.app.ActivityTaskManager
import android.content.ComponentName
import android.content.pm.IPackageManager
import android.content.pm.PackageManager
import android.os.RemoteException
import android.platform.test.annotations.Postsubmit
import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl.supportsMultiInstance
import org.junit.Assert.assertEquals
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

/**
 * Unit tests for LauncherAppsService
 * Run: atest LauncherAppsServiceTest
 */
@Postsubmit
@RunWith(AndroidJUnit4::class)
class LauncherAppsServiceTest {

    val pm = mock<IPackageManager>()

    @Before
    fun setup() {
        assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(
            InstrumentationRegistry.getInstrumentation().getTargetContext()))
    }

    @Test
    @Throws(RemoteException::class)
    fun supportsMultiInstanceSplit_activityPropertyTrue() {
        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
        val activityProp = PackageManager.Property("", true, "", "")
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER)))
            .thenReturn(activityProp)
        val appProp = PackageManager.Property("", false, "", "")
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(null), eq(TEST_OTHER_USER)))
            .thenReturn(appProp)

        // Expect activity property to override application property
        assertEquals(true, supportsMultiInstance(pm, component, TEST_OTHER_USER))
    }

    @Test
    @Throws(RemoteException::class)
    fun supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue() {
        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
        val activityProp = PackageManager.Property("", false, "", "")
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER)))
            .thenReturn(activityProp)
        val appProp = PackageManager.Property("", true, "", "")
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(null), eq(TEST_OTHER_USER)))
            .thenReturn(appProp)

        // Expect activity property to override application property
        assertEquals(false, supportsMultiInstance(pm, component, TEST_OTHER_USER))
    }

    @Test
    @Throws(RemoteException::class)
    fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER)))
            .thenThrow(RemoteException())
        val appProp = PackageManager.Property("", true, "", "")
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(null), eq(TEST_OTHER_USER)))
            .thenReturn(appProp)

        // Expect fall through to app property
        assertEquals(true, supportsMultiInstance(pm, component, TEST_OTHER_USER))
    }

    @Test
    @Throws(RemoteException::class)
    fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER)))
            .thenThrow(RemoteException())
        whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
            eq(component.packageName), eq(null), eq(TEST_OTHER_USER)))
            .thenThrow(RemoteException())

        assertEquals(false, supportsMultiInstance(pm, component, TEST_OTHER_USER))
    }

    companion object {
        val TEST_PACKAGE = "com.android.server.pm"
        val TEST_ACTIVITY = "TestActivity"
        val TEST_OTHER_USER = 1234
    }
}
 No newline at end of file