1 // Copyright 2016 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.instantapps; 6 7 import android.app.Instrumentation; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.content.IntentFilter; 11 import android.content.SharedPreferences; 12 import android.net.Uri; 13 import android.nfc.NfcAdapter; 14 import android.provider.Browser; 15 import android.support.test.InstrumentationRegistry; 16 17 import androidx.test.filters.SmallTest; 18 19 import org.junit.Assert; 20 import org.junit.Before; 21 import org.junit.Rule; 22 import org.junit.Test; 23 import org.junit.runner.RunWith; 24 25 import org.chromium.base.ContextUtils; 26 import org.chromium.base.test.util.CommandLineFlags; 27 import org.chromium.chrome.browser.IntentHandler; 28 import org.chromium.chrome.browser.ShortcutHelper; 29 import org.chromium.chrome.browser.flags.ChromeSwitches; 30 import org.chromium.chrome.browser.tab.Tab; 31 import org.chromium.chrome.test.ChromeJUnit4ClassRunner; 32 import org.chromium.chrome.test.ChromeTabbedActivityTestRule; 33 import org.chromium.content_public.browser.test.util.TestThreadUtils; 34 35 /** 36 * Unit tests for {@link InstantAppsHandler}. 37 */ 38 @RunWith(ChromeJUnit4ClassRunner.class) 39 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) 40 public class InstantAppsHandlerTest { 41 @Rule 42 public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); 43 44 private TestInstantAppsHandler mHandler; 45 private Context mContext; 46 47 private static final Uri URI = Uri.parse("http://sampleurl.com/foo"); 48 private static final String INSTANT_APP_URL = "http://sampleapp.com/boo"; 49 private static final Uri REFERRER_URI = Uri.parse("http://www.wikipedia.org/"); 50 createViewIntent()51 private Intent createViewIntent() { 52 return new Intent(Intent.ACTION_VIEW, URI); 53 } 54 55 @Before setUp()56 public void setUp() throws Exception { 57 mActivityTestRule.startMainActivityOnBlankPage(); 58 59 mContext = InstrumentationRegistry.getTargetContext(); 60 mHandler = new TestInstantAppsHandler(); 61 62 SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); 63 SharedPreferences.Editor editor = prefs.edit(); 64 editor.putBoolean("applink.app_link_enabled", true); 65 editor.putBoolean("applink.chrome_default_browser", true); 66 editor.apply(); 67 } 68 69 @Test 70 @SmallTest testInstantAppsDisabled_incognito()71 public void testInstantAppsDisabled_incognito() { 72 Intent i = createViewIntent(); 73 i.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true); 74 75 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, i, false, true)); 76 } 77 78 @Test 79 @SmallTest testInstantAppsDisabled_doNotLaunch()80 public void testInstantAppsDisabled_doNotLaunch() { 81 Intent i = createViewIntent(); 82 i.putExtra("com.google.android.gms.instantapps.DO_NOT_LAUNCH_INSTANT_APP", true); 83 84 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, i, false, true)); 85 } 86 87 @Test 88 @SmallTest testInstantAppsDisabled_mainIntent()89 public void testInstantAppsDisabled_mainIntent() { 90 Intent i = new Intent(Intent.ACTION_MAIN); 91 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, i, false, true)); 92 } 93 94 @Test 95 @SmallTest testInstantAppsDisabled_intentOriginatingFromChrome()96 public void testInstantAppsDisabled_intentOriginatingFromChrome() { 97 Intent i = createViewIntent(); 98 i.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName()); 99 100 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, i, false, true)); 101 102 Intent signedIntent = createViewIntent(); 103 signedIntent.setPackage(mContext.getPackageName()); 104 IntentHandler.addTrustedIntentExtras(signedIntent); 105 106 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, signedIntent, false, true)); 107 } 108 109 @Test 110 @SmallTest testInstantAppsDisabled_launchFromShortcut()111 public void testInstantAppsDisabled_launchFromShortcut() { 112 Intent i = createViewIntent(); 113 i.putExtra(ShortcutHelper.EXTRA_SOURCE, 1); 114 Assert.assertFalse(mHandler.handleIncomingIntent(mContext, i, false, true)); 115 } 116 117 @Test 118 @SmallTest testChromeNotDefault()119 public void testChromeNotDefault() { 120 SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); 121 SharedPreferences.Editor editor = prefs.edit(); 122 editor.putBoolean("applink.chrome_default_browser", false); 123 editor.apply(); 124 125 Assert.assertFalse( 126 mHandler.handleIncomingIntent(mContext, createViewIntent(), false, true)); 127 128 // Even if Chrome is not default, launch Instant Apps for CustomTabs since those never 129 // show disambiguation dialogs. 130 Intent cti = createViewIntent() 131 .putExtra("android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS", true); 132 Assert.assertTrue(mHandler.handleIncomingIntent(mContext, cti, true, true)); 133 } 134 135 @Test 136 @SmallTest testInstantAppsEnabled()137 public void testInstantAppsEnabled() { 138 Intent i = createViewIntent(); 139 Assert.assertTrue(mHandler.handleIncomingIntent( 140 InstrumentationRegistry.getContext(), i, false, true)); 141 142 // Check that identical intent wouldn't be enabled for CustomTab flow. 143 Assert.assertFalse( 144 mHandler.handleIncomingIntent(InstrumentationRegistry.getContext(), i, true, true)); 145 146 // Add CustomTab specific extra and check it's now enabled. 147 i.putExtra("android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS", true); 148 Assert.assertTrue( 149 mHandler.handleIncomingIntent(InstrumentationRegistry.getContext(), i, true, true)); 150 } 151 152 @Test 153 @SmallTest testNfcIntent()154 public void testNfcIntent() { 155 Intent i = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED); 156 i.setData(Uri.parse("http://instantapp.com/")); 157 Assert.assertTrue(mHandler.handleIncomingIntent( 158 InstrumentationRegistry.getContext(), i, false, true)); 159 } 160 161 @Test 162 @SmallTest testHandleNavigation_startAsyncCheck()163 public void testHandleNavigation_startAsyncCheck() { 164 TestThreadUtils.runOnUiThreadBlocking( 165 () -> Assert.assertFalse( 166 mHandler.handleNavigation(mContext, INSTANT_APP_URL, REFERRER_URI, 167 mActivityTestRule.getActivity() 168 .getTabModelSelector() 169 .getCurrentTab()))); 170 Assert.assertFalse(mHandler.mLaunchInstantApp); 171 Assert.assertTrue(mHandler.mStartedAsyncCall); 172 } 173 174 @Test 175 @SmallTest testLaunchFromBanner()176 public void testLaunchFromBanner() { 177 // Intent to supervisor 178 final Intent i = new Intent(Intent.ACTION_MAIN); 179 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 180 181 Instrumentation.ActivityMonitor monitor = 182 InstrumentationRegistry.getInstrumentation().addMonitor( 183 new IntentFilter(Intent.ACTION_MAIN), null, true); 184 185 TestThreadUtils.runOnUiThreadBlocking( 186 () -> mHandler.launchFromBanner(new InstantAppsBannerData("App", null, 187 INSTANT_APP_URL, REFERRER_URI, i, "Launch", 188 mActivityTestRule.getActivity() 189 .getTabModelSelector() 190 .getCurrentTab() 191 .getWebContents(), 192 false))); 193 194 // Started instant apps intent 195 Assert.assertEquals(1, monitor.getHits()); 196 197 Assert.assertEquals(REFERRER_URI, i.getParcelableExtra(Intent.EXTRA_REFERRER)); 198 Assert.assertTrue(i.getBooleanExtra(InstantAppsHandler.IS_REFERRER_TRUSTED_EXTRA, false)); 199 Assert.assertTrue( 200 i.getBooleanExtra(InstantAppsHandler.IS_USER_CONFIRMED_LAUNCH_EXTRA, false)); 201 Assert.assertEquals(mContext.getPackageName(), 202 i.getStringExtra(InstantAppsHandler.TRUSTED_REFERRER_PKG_EXTRA)); 203 204 // After a banner launch, test that the next launch happens automatically 205 206 TestThreadUtils.runOnUiThreadBlocking( 207 () -> Assert.assertTrue( 208 mHandler.handleNavigation(mContext, INSTANT_APP_URL, REFERRER_URI, 209 mActivityTestRule.getActivity() 210 .getTabModelSelector() 211 .getCurrentTab()))); 212 Assert.assertFalse(mHandler.mStartedAsyncCall); 213 Assert.assertTrue(mHandler.mLaunchInstantApp); 214 } 215 216 static class TestInstantAppsHandler extends InstantAppsHandler { 217 // Keeps track of whether startCheckForInstantApps() has been called. 218 public volatile boolean mStartedAsyncCall; 219 // Keeps track of whether launchInstantAppForNavigation() has been called. 220 public volatile boolean mLaunchInstantApp; 221 222 @Override tryLaunchingInstantApp(Context context, Intent intent, boolean isCustomTabsIntent, Intent fallbackIntent)223 protected boolean tryLaunchingInstantApp(Context context, Intent intent, 224 boolean isCustomTabsIntent, Intent fallbackIntent) { 225 return true; 226 } 227 228 @Override launchInstantAppForNavigation(Context context, String url, Uri referrer)229 protected boolean launchInstantAppForNavigation(Context context, String url, Uri referrer) { 230 mLaunchInstantApp = true; 231 return true; 232 } 233 234 @Override maybeShowInstantAppBanner( Context context, String url, Uri referrer, Tab tab, boolean instantAppIsDefault)235 protected void maybeShowInstantAppBanner( 236 Context context, String url, Uri referrer, Tab tab, boolean instantAppIsDefault) { 237 mStartedAsyncCall = true; 238 } 239 } 240 } 241