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

Commit 7cb34cc9 authored by Jackal Guo's avatar Jackal Guo
Browse files

Avoid showing the disambiguation dialog

Don't show the disambiguation dialog if there is a preferred one and
the new package which is installed with INSTALL_REASON_DEVICE_SETUP.

Bug: 176933368
Test: atest PreferredComponentTest
Test: using 'pm install --install-reason 3' to install another intent
      filter and check if disambiguation dialog shows.
Change-Id: I1bf714192627d147e5df952ff536033ccd2fecb1
parent d92ef806
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -3121,7 +3121,7 @@ public class ComputerEngine implements Computer {
                final ResolveInfo ri = query.get(j);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Match for " + ri.activityInfo
                            + ": 0x" + Integer.toHexString(match));
                            + ": 0x" + Integer.toHexString(ri.match));
                }
                if (ri.match > match) {
                    match = ri.match;
@@ -3213,7 +3213,8 @@ public class ComputerEngine implements Computer {
                    // clear it and re-ask the user their preference, if we're looking for
                    // an "always" type entry.

                    if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
                    if (always
                            && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity, userId)) {
                        if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
                            if (allowSetMutation) {
                                // some components of the set are no longer present in
+13 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.pm;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.util.Slog;
@@ -27,6 +28,7 @@ import android.util.TypedXmlSerializer;

import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import com.android.server.pm.pkg.PackageUserState;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -197,7 +199,7 @@ public class PreferredComponent {
        }
    }

    public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
    public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage, int userId) {
        if (mSetPackages == null) {
            return query == null;
        }
@@ -206,6 +208,7 @@ public class PreferredComponent {
        }
        final int NQ = query.size();
        final int NS = mSetPackages.length;
        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
        int numMatch = 0;
        for (int i=0; i<NQ; i++) {
            ResolveInfo ri = query.get(i);
@@ -218,6 +221,15 @@ public class PreferredComponent {
                continue;
            }

            // Avoid showing the disambiguation dialog if the package which is installed with
            // reason INSTALL_REASON_DEVICE_SETUP.
            final PackageUserState pkgUserState =
                    pmi.getPackageStateInternal(ai.packageName).getUserStates().get(userId);
            if (pkgUserState != null && pkgUserState.getInstallReason()
                    == PackageManager.INSTALL_REASON_DEVICE_SETUP) {
                continue;
            }

            for (int j=0; j<NS; j++) {
                if (mSetPackages[j].equals(ai.packageName)
                        && mSetClasses[j].equals(ai.name)) {
+7 −6
Original line number Diff line number Diff line
@@ -55,11 +55,7 @@ import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedProcess;

import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import android.content.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SuspendParams;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -117,6 +113,9 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationPersistence;
@@ -3292,7 +3291,7 @@ public final class Settings implements Watchable, Snappable {
        int systemMatch = 0;
        int thirdPartyMatch = 0;
        final int numMatches = (ri == null ? 0 : ri.size());
        if (numMatches <= 1) {
        if (numMatches < 1) {
            Slog.w(TAG, "No potential matches found for " + intent
                    + " while setting preferred " + cn.flattenToShortString());
            return;
@@ -4764,7 +4763,9 @@ public final class Settings implements Watchable, Snappable {
            pw.print(" instant=");
            pw.print(ps.getInstantApp(user.id));
            pw.print(" virtual=");
            pw.println(ps.getVirtualPreload(user.id));
            pw.print(ps.getVirtualPreload(user.id));
            pw.print(" installReason=");
            pw.println(ps.getInstallReason(user.id));

            if (ps.getSuspended(user.id)) {
                pw.print(prefix);
+153 −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 com.android.server.pm;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;

import com.android.server.LocalServices;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateImpl;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.ArrayList;
import java.util.List;

@Presubmit
@RunWith(JUnit4.class)
public class PreferredComponentTest {

    private PackageManagerInternal mMockPackageManagerInternal;

    @Before
    public void setUp() {
        mMockPackageManagerInternal = mock(PackageManagerInternal.class);
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
    }

    @Test
    public void testPreferredComponent_sameSet_withAppInstalledByDeviceSetup() {
        // Assume we have two ResolveInfos that handle the same Intent.
        final int userId = UserHandle.USER_SYSTEM;
        final List<ResolveInfo> query = new ArrayList<>();
        query.add(createResolveInfo(0, userId));
        query.add(createResolveInfo(1, userId));
        // ResolveInfo(0) is already set as the preferred one when only it exists.
        final ComponentName component = query.get(0).getComponentInfo().getComponentName();
        final PreferredActivity pa = new PreferredActivity(new IntentFilter("TEST_ACTION"),
                0 /* match */, new ComponentName[]{component}, component, true /* always */);

        // Assume ResolveInfo(0) is preinstalled, and ResolveInfo(1) is installed when device setup.
        final PackageUserState pkgUserState0 = new PackageUserStateImpl().setInstallReason(
                PackageManager.INSTALL_REASON_UNKNOWN);
        final PackageUserState pkgUserState1 = new PackageUserStateImpl().setInstallReason(
                PackageManager.INSTALL_REASON_DEVICE_SETUP);
        final PackageStateInternal psInt0 = mock(PackageStateInternal.class);
        final PackageStateInternal psInt1 = mock(PackageStateInternal.class);
        final SparseArray<Object> userStates0 = mock(SparseArray.class);
        final SparseArray<Object> userStates1 = mock(SparseArray.class);
        doReturn(psInt0).when(mMockPackageManagerInternal).getPackageStateInternal("foo_bar0");
        doReturn(psInt1).when(mMockPackageManagerInternal).getPackageStateInternal("foo_bar1");
        doReturn(userStates0).when(psInt0).getUserStates();
        doReturn(userStates1).when(psInt1).getUserStates();
        doReturn(pkgUserState0).when(userStates0).get(anyInt());
        doReturn(pkgUserState1).when(userStates1).get(anyInt());

        // Check if ResolveInfo(1) which is installed by device setup affects the preferred set and
        // this may trigger disambiguation dialog.
        assertTrue(pa.mPref.sameSet(query, true /* excludeSetupWizardPackage */, userId));
    }

    @Test
    public void testPreferredComponent_notSameSet_withAppNotInstalledByDeviceSetup() {
        // Assume we have two ResolveInfos that handle the same Intent.
        final int userId = UserHandle.USER_SYSTEM;
        final List<ResolveInfo> query = new ArrayList<>();
        query.add(createResolveInfo(0, userId));
        query.add(createResolveInfo(1, userId));
        // ResolveInfo(0) is already set as the preferred one when only it exists.
        final ComponentName component = query.get(0).getComponentInfo().getComponentName();
        final PreferredActivity pa = new PreferredActivity(new IntentFilter("TEST_ACTION"),
                0 /* match */, new ComponentName[]{component}, component, true /* always */);

        // Assume ResolveInfo(0) is preinstalled, and ResolveInfo(1) is installed by user.
        final PackageUserState pkgUserState0 = new PackageUserStateImpl().setInstallReason(
                PackageManager.INSTALL_REASON_UNKNOWN);
        final PackageUserState pkgUserState1 = new PackageUserStateImpl().setInstallReason(
                PackageManager.INSTALL_REASON_USER);
        final PackageStateInternal psInt0 = mock(PackageStateInternal.class);
        final PackageStateInternal psInt1 = mock(PackageStateInternal.class);
        final SparseArray<Object> userStates0 = mock(SparseArray.class);
        final SparseArray<Object> userStates1 = mock(SparseArray.class);
        doReturn(psInt0).when(mMockPackageManagerInternal).getPackageStateInternal("foo_bar0");
        doReturn(psInt1).when(mMockPackageManagerInternal).getPackageStateInternal("foo_bar1");
        doReturn(userStates0).when(psInt0).getUserStates();
        doReturn(userStates1).when(psInt1).getUserStates();
        doReturn(pkgUserState0).when(userStates0).get(anyInt());
        doReturn(pkgUserState1).when(userStates1).get(anyInt());

        // Check if ResolveInfo(1) which is installed by user affects the preferred set and
        // this may trigger disambiguation dialog.
        assertFalse(pa.mPref.sameSet(query, true /* excludeSetupWizardPackage */, userId));
    }

    private static ResolveInfo createResolveInfo(int i, int userId) {
        final ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.activityInfo = createActivityInfo(i);
        resolveInfo.targetUserId = userId;
        return resolveInfo;
    }

    private static ActivityInfo createActivityInfo(int i) {
        final ActivityInfo ai = new ActivityInfo();
        ai.name = "activity_name" + i;
        ai.packageName = "foo_bar" + i;
        ai.enabled = true;
        ai.exported = true;
        ai.permission = null;
        ai.applicationInfo = createApplicationInfo(i, ai.packageName);
        return ai;
    }

    private static ApplicationInfo createApplicationInfo(int i, String packageName) {
        final ApplicationInfo ai = new ApplicationInfo();
        ai.name = "app_name" + i;
        ai.packageName = packageName;
        ai.enabled = true;
        return ai;
    }
}