Commit f3411ce8 authored by Juan Mojica's avatar Juan Mojica Committed by Commit Bot

Create minimum AGSA version check for using direct intent.

Also, use GSAState helper methods to fetch AGSA version names.

Bug: 1098431
Change-Id: Ic369d1fbdb08df74dfffacc29592408f74fb9b97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2522883
Commit-Queue: Juan Mojica <juanmojica@google.com>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarBen Goldberger <benwgold@google.com>
Cr-Commit-Position: refs/heads/master@{#826592}
parent c36ac671
......@@ -4,11 +4,8 @@
package org.chromium.chrome.browser.share;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
......@@ -19,6 +16,7 @@ import org.chromium.base.ContextUtils;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.gsa.GSAState;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.IdentityServicesProvider;
......@@ -46,6 +44,8 @@ public class LensUtils {
private static final String MIN_AGSA_VERSION_FEATURE_PARAM_NAME = "minAgsaVersionName";
private static final String MIN_AGSA_VERSION_SHOPPING_FEATURE_PARAM_NAME =
"minAgsaVersionNameForShopping";
private static final String MIN_AGSA_VERSION_DIRECT_INTENT_FEATURE_PARAM_NAME =
"minAgsaVersionForDirectIntent";
private static final String USE_SEARCH_BY_IMAGE_TEXT_FEATURE_PARAM_NAME =
"useSearchByImageText";
private static final String LENS_SHOPPING_ALLOWLIST_ENTRIES_FEATURE_PARAM_NAME =
......@@ -62,6 +62,7 @@ public class LensUtils {
"orderShareImageBeforeLens";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "10.65";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT = "11.16";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_DIRECT_INTENT = "11.34";
private static final int LENS_INTENT_TYPE_LENS_CHROME_SHOPPING = 18;
private static final String LENS_SHOPPING_FEATURE_FLAG_VARIANT_NAME = "lensShopVariation";
private static final String LENS_DEFAULT_SHOPPING_URL_PATTERNS =
......@@ -72,6 +73,7 @@ public class LensUtils {
*/
private static boolean sFakePassableLensEnvironmentForTesting;
private static boolean sFakeImageUrlInShoppingAllowlistForTesting;
private static String sFakeInstalledAgsaVersion;
private static String sFakeVariationsForTesting;
/*
......@@ -89,6 +91,15 @@ public class LensUtils {
sFakeImageUrlInShoppingAllowlistForTesting = shouldFake;
}
/**
* Sets a fake installed agsa version name. Used by test cases to set versions below and above
* minimum required agsa versions.
*/
@VisibleForTesting
public static void setFakeInstalledAgsaVersion(final String fakeAgsaVersionName) {
sFakeInstalledAgsaVersion = fakeAgsaVersionName;
}
/*
* If set, short-circuit the JNI call to retrieve the variation IDs. Used by
* test cases.
......@@ -109,7 +120,6 @@ public class LensUtils {
* available.
*/
public static String getLensActivityVersionNameIfAvailable(final Context context) {
// Use this syntax to avoid NPE if unset.
if (Boolean.TRUE.equals(sFakePassableLensEnvironmentForTesting)) {
// Returns the minimum version which will meet the bar and allow future AGSA
// version
......@@ -119,24 +129,14 @@ public class LensUtils {
}
return MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE;
} else {
try {
final PackageManager pm = context.getPackageManager();
// No data transmission occurring so safe to assume incognito is false.
final Intent lensIntent = getShareWithGoogleLensIntent(Uri.EMPTY,
/* isIncognito= */ false,
/* currentTimeNanos= */ 0L, /* srcUrl */ "",
/* titleOrAltText */ "", /* pageUrl */ "", /* lensQueryResult */ null,
/* requiresConfirmation */ false);
final ComponentName lensActivity = lensIntent.resolveActivity(pm);
if (lensActivity == null) return "";
final PackageInfo packageInfo = pm.getPackageInfo(lensActivity.getPackageName(), 0);
if (packageInfo == null) {
return "";
} else {
return packageInfo.versionName;
}
} catch (final PackageManager.NameNotFoundException e) {
if (context == null) {
return "";
}
String agsaVersion = GSAState.getInstance(context).getAgsaVersionName();
if (agsaVersion == null) {
return "";
} else {
return agsaVersion;
}
}
}
......@@ -193,6 +193,32 @@ public class LensUtils {
return "";
}
/**
* Gets the minimum AGSA version required to support the URI-based direct intentType
* integration on this device. Takes the value from a server provided value if a
* field trial is active but otherwise will take the value from a client side
* default (unless the lens feature is not enabled at all, in which case return
* an empty string).
*
* @return The minimum version name string or an empty string if not available.
*/
public static String getMinimumAgsaVersionForDirectIntentSupport() {
// Shopping feature AGSA version takes priority over Search with Google Lens
if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS)) {
final String serverProvidedMinAgsaVersion =
ChromeFeatureList.getFieldTrialParamByFeature(
ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS,
MIN_AGSA_VERSION_DIRECT_INTENT_FEATURE_PARAM_NAME);
if (TextUtils.isEmpty(serverProvidedMinAgsaVersion)) {
// Falls into this block if the user enabled the feature using chrome://flags
// and the param was not set by the server.
return MIN_AGSA_VERSION_NAME_FOR_LENS_DIRECT_INTENT;
}
return serverProvidedMinAgsaVersion;
}
return "";
}
/**
* Checks whether the device is below Android O. We restrict to these versions
* to limit to OS"s where image processing vulnerabilities can be retroactively
......@@ -239,10 +265,9 @@ public class LensUtils {
* @param requiresConfirmation Whether the request requires an confirmation dialog.
* @return The intent to Google Lens.
*/
public static Intent getShareWithGoogleLensIntent(final Uri imageUri, final boolean isIncognito,
final long currentTimeNanos, final String srcUrl, final String titleOrAltText,
final String pageUrl, LensQueryResult lensQueryResult,
public static Intent getShareWithGoogleLensIntent(final Context context, final Uri imageUri,
final boolean isIncognito, final long currentTimeNanos, final String srcUrl,
final String titleOrAltText, final String pageUrl, LensQueryResult lensQueryResult,
final boolean requiresConfirmation) {
final CoreAccountInfo coreAccountInfo =
IdentityServicesProvider.get()
......@@ -252,9 +277,8 @@ public class LensUtils {
// information to Lens.
final String signedInAccountName =
(coreAccountInfo == null || isIncognito) ? "" : coreAccountInfo.getEmail();
Uri lensUri = useDirectIntent() ? Uri.parse(LENS_DIRECT_INTENT_CONTRACT_URI)
: Uri.parse(LENS_CONTRACT_URI);
Uri lensUri = useDirectIntent(context) ? Uri.parse(LENS_DIRECT_INTENT_CONTRACT_URI)
: Uri.parse(LENS_CONTRACT_URI);
if (!Uri.EMPTY.equals(imageUri)) {
final Uri.Builder lensUriBuilder =
lensUri.buildUpon()
......@@ -340,12 +364,20 @@ public class LensUtils {
/**
* Enables the starting of LenActivity directly, rather than going through the Lens
* session running in AGSA.
* session running in AGSA. Also checks if the required AGSA version for direct intent
* is below or equal to the provided version.
*/
public static boolean useDirectIntent() {
public static boolean useDirectIntent(final Context context) {
// TODO(https://crbug.com/1146591): Refactor GSA state checks to avoid multiple version
// grabs.
String agsaVersionName = sFakeInstalledAgsaVersion != null
? sFakeInstalledAgsaVersion
: getLensActivityVersionNameIfAvailable(context);
return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS,
USE_DIRECT_INTENT_FEATURE_PARAM_NAME, false);
ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS,
USE_DIRECT_INTENT_FEATURE_PARAM_NAME, false)
&& !GSAState.getInstance(context).isAgsaVersionBelowMinimum(
agsaVersionName, getMinimumAgsaVersionForDirectIntentSupport());
}
/**
......
......@@ -133,9 +133,10 @@ public class ShareHelper extends org.chromium.components.browser_ui.share.ShareH
public static void shareImageWithGoogleLens(final WindowAndroid window, Uri imageUri,
boolean isIncognito, String srcUrl, String titleOrAltText, String pageUrl,
LensQueryResult lensQueryResult, boolean requiresConfirmation) {
Intent shareIntent = LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito,
SystemClock.elapsedRealtimeNanos(), srcUrl, titleOrAltText, pageUrl,
lensQueryResult, requiresConfirmation);
Intent shareIntent =
LensUtils.getShareWithGoogleLensIntent(ContextUtils.getApplicationContext(),
imageUri, isIncognito, SystemClock.elapsedRealtimeNanos(), srcUrl,
titleOrAltText, pageUrl, lensQueryResult, requiresConfirmation);
try {
// Pass an empty callback to ensure the triggered activity can identify the source
// of the intent (startActivityForResult allows app identification).
......
......@@ -5,18 +5,27 @@
package org.chromium.chrome.browser.share;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.lens.LensQueryResult;
......@@ -35,6 +44,24 @@ public class LensUtilsTest {
@Rule
public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
private static final String TEST_MIN_AGSA_VERSION = "11.34";
private static final String TEST_MIN_AGSA_VERSION_BELOW_DIRECT_INTENT_MIN = "11.33.9";
@Mock
Context mContext;
@Mock
PackageManager mPackageManager;
@Mock
PackageInfo mPackageInfo;
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(IntentHandler.PACKAGE_GSA, 0);
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is signed
* in.
......@@ -287,7 +314,7 @@ public class LensUtilsTest {
public void
getShareWithGoogleLensIntentSignedInTest_directIntentEnabled() {
mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
LensUtils.setFakeInstalledAgsaVersion(TEST_MIN_AGSA_VERSION);
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* pageUrl */ null,
......@@ -313,6 +340,85 @@ public class LensUtilsTest {
intentWithContentUri.getAction());
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is signed
* in and the direct intent experiment is enabled, but the AGSA version is empty.
*/
@CommandLineFlags.Add({"enable-features="
+ ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName",
"force-fieldtrials=FakeStudyName/Enabled",
"force-fieldtrial-params=FakeStudyName.Enabled:useDirectIntent/true"})
@Test
@SmallTest
public void
getShareWithGoogleLensIntentSignedInTest_directIntentEnabledAgsaVersionEmpty() {
mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
LensUtils.setFakeInstalledAgsaVersion("");
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* pageUrl */ null,
/* lensQueryResult */ null,
/* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString());
Assert.assertEquals("Intent without image has incorrect action", Intent.ACTION_VIEW,
intentNoUri.getAction());
final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* pageUrl */ null,
/* lensQueryResult */ null,
/* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url"
+ "&AccountNameUriKey=test%40gmail.com&IncognitoUriKey=false"
+ "&ActivityLaunchTimestampNanos=1234",
intentWithContentUri.getData().toString());
Assert.assertEquals("Intent with image has incorrect action", Intent.ACTION_VIEW,
intentWithContentUri.getAction());
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is signed
* in and the direct intent experiment is enabled, but the AGSA version is below the minimum
* required for direct intent.
*/
@CommandLineFlags.Add({"enable-features="
+ ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName",
"force-fieldtrials=FakeStudyName/Enabled",
"force-fieldtrial-params=FakeStudyName.Enabled:useDirectIntent/true"})
@Test
@SmallTest
public void
getShareWithGoogleLensIntentSignedInTest_directIntentEnabledAgsaVersionBelowMinimum() {
mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
LensUtils.setFakeInstalledAgsaVersion(TEST_MIN_AGSA_VERSION_BELOW_DIRECT_INTENT_MIN);
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* pageUrl */ null,
/* lensQueryResult */ null,
/* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString());
Assert.assertEquals("Intent without image has incorrect action", Intent.ACTION_VIEW,
intentNoUri.getAction());
final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* pageUrl */ null,
/* lensQueryResult */ null,
/* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url"
+ "&AccountNameUriKey=test%40gmail.com&IncognitoUriKey=false"
+ "&ActivityLaunchTimestampNanos=1234",
intentWithContentUri.getData().toString());
Assert.assertEquals("Intent with image has incorrect action", Intent.ACTION_VIEW,
intentWithContentUri.getAction());
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is
* incognito.
......@@ -451,7 +557,7 @@ public class LensUtilsTest {
LensQueryResult lensQueryResult, boolean requiresConfirmation) {
return TestThreadUtils.runOnUiThreadBlockingNoException(
()
-> LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito,
-> LensUtils.getShareWithGoogleLensIntent(mContext, imageUri, isIncognito,
currentTimeNanos, srcUrl, titleOrAltText, pageUrl, lensQueryResult,
requiresConfirmation));
}
......@@ -791,4 +897,4 @@ public class LensUtilsTest {
return TestThreadUtils.runOnUiThreadBlockingNoException(
() -> LensUtils.enableImageChip(isIncognito));
}
}
\ No newline at end of file
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment