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