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
This commit is contained in:
Jackal Guo
2021-11-08 10:22:08 +08:00
parent d92ef8062f
commit 7cb34cc9f0
4 changed files with 176 additions and 9 deletions

View File

@@ -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

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -0,0 +1,153 @@
/*
* 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;
}
}