1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.matchflags.cts; 18 19 import static org.junit.Assert.fail; 20 import static org.junit.Assume.assumeFalse; 21 22 import android.content.ActivityNotFoundException; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.net.Uri; 26 27 import androidx.test.ext.junit.runners.AndroidJUnit4; 28 import androidx.test.platform.app.InstrumentationRegistry; 29 30 import com.android.compatibility.common.util.FeatureUtil; 31 import com.android.compatibility.common.util.ShellUtils; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Rule; 36 import org.junit.Test; 37 import org.junit.rules.TestName; 38 import org.junit.runner.RunWith; 39 40 @RunWith(AndroidJUnit4.class) 41 public class MatchFlagTests { 42 43 private static final String ONLY_BROWSER_URI = 44 "https://nohandler-02xgpcssu1v7xvpek0skc905glnyu7ihjtza3eufox0mauqyri.com"; 45 private static final String UNIQUE_URI = 46 "https://unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com"; 47 48 private static final String SHARED_PKG_NAME = "android.matchflags.app.shared"; 49 private static final String UNIQUE_AND_SHARED_PKG_NAME = 50 "android.matchflags.app.uniqueandshared"; 51 52 @Rule 53 public TestName name = new TestName(); 54 55 @Before 56 @After removeApprovals()57 public void removeApprovals() { 58 setDomainUserSelectionApproval(false); 59 } 60 61 @Test startNoBrowserIntentWithNoMatchingApps()62 public void startNoBrowserIntentWithNoMatchingApps() throws Exception { 63 assumeFalse("Skipping test for watch", 64 InstrumentationRegistry.getInstrumentation().getTargetContext() 65 .getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 66 67 Intent onlyBrowserIntent = new Intent(Intent.ACTION_VIEW) 68 .addCategory(Intent.CATEGORY_BROWSABLE) 69 .setData(Uri.parse(ONLY_BROWSER_URI)); 70 71 if (isBrowserPresent(true)) { 72 startActivity(onlyBrowserIntent); 73 } else { 74 try { 75 startActivity(onlyBrowserIntent); 76 fail("Device without browser should not launch browser only intent"); 77 } catch (ActivityNotFoundException e) { 78 // hooray 79 } 80 } 81 82 Intent noBrowserWithBrowserOnlyIntent = new Intent(onlyBrowserIntent) 83 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER); 84 85 try { 86 startActivity(noBrowserWithBrowserOnlyIntent); 87 fail("Should not have started a browser-only intent with NON_BROWSER flag set"); 88 } catch (ActivityNotFoundException e) { 89 // hooray! 90 } 91 } 92 93 @Test startRequireDefaultWithNoDefault()94 public void startRequireDefaultWithNoDefault() throws Exception { 95 Intent sharedIntent = new Intent("android.matchflags.app.SHARED_ACTION"); 96 97 startActivity(sharedIntent); 98 99 Intent sharedIntentRequireDefault = new Intent(sharedIntent) 100 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT); 101 102 try { 103 startActivity(sharedIntentRequireDefault); 104 fail("Should have started intent with no default when default required"); 105 } catch (ActivityNotFoundException e) { 106 // hooray! 107 } 108 } 109 110 @Test startRequireDefaultWithSingleMatch()111 public void startRequireDefaultWithSingleMatch() throws Exception { 112 Intent uniqueIntent = new Intent("android.matchflags.app.UNIQUE_ACTION") 113 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT); 114 startActivity(uniqueIntent); 115 } 116 117 @Test startNoBrowserRequireDefault()118 public void startNoBrowserRequireDefault() throws Exception { 119 setDomainUserSelectionApproval(true); 120 startNoBrowserRequireDefaultInternal(true); 121 } 122 123 @Test startNoBrowserRequireDefaultUnapproved()124 public void startNoBrowserRequireDefaultUnapproved() throws Exception { 125 assumeFalse("Skipping test for watch", FeatureUtil.isWatch()); 126 setDomainUserSelectionApproval(false); 127 startNoBrowserRequireDefaultInternal(false); 128 } 129 startNoBrowserRequireDefaultInternal(boolean isDomainApproved)130 private void startNoBrowserRequireDefaultInternal(boolean isDomainApproved) { 131 Intent uniqueUriIntent = new Intent(Intent.ACTION_VIEW) 132 .addCategory(Intent.CATEGORY_BROWSABLE) 133 .setData(Uri.parse(UNIQUE_URI)); 134 135 startActivity(uniqueUriIntent); 136 137 Intent uniqueUriIntentNoBrowserRequireDefault = new Intent(uniqueUriIntent) 138 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER) 139 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT); 140 141 if (isBrowserPresent(false)) { 142 // with non-browser, we'd expect the resolver 143 // with require default, we'll get activity not found 144 try { 145 startActivity(uniqueUriIntentNoBrowserRequireDefault); 146 if (!isDomainApproved) { 147 fail("Should fail to launch when started with non-browser and require default" 148 + " when browser present"); 149 } 150 } catch (ActivityNotFoundException e) { 151 // hooray! 152 if (isDomainApproved) { 153 // Domain approval should force only the test Activity to be returned, which 154 // means it should pass the above flags and launch. 155 fail("Should succeed launch when started with non-browser and require default" 156 + " when browser present"); 157 } 158 } 159 } else { 160 // with non-browser, but no browser present, we'd get a single result 161 // with require default, we'll resolve to that single result 162 try { 163 startActivity(uniqueUriIntentNoBrowserRequireDefault); 164 if (!isDomainApproved) { 165 fail("Should fail to launch when started with non-browser and require default" 166 + " when browser not present"); 167 } 168 } catch (ActivityNotFoundException e) { 169 if (isDomainApproved) { 170 fail("Should succeed launch when started with non-browser and require default" 171 + " when browser not present"); 172 } 173 } 174 } 175 } 176 isBrowserPresent(boolean includeFallback)177 private static boolean isBrowserPresent(boolean includeFallback) { 178 return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager() 179 .queryIntentActivities(new Intent(Intent.ACTION_VIEW).addCategory( 180 Intent.CATEGORY_BROWSABLE).setData(Uri.parse(ONLY_BROWSER_URI)), 181 0 /* flags */) 182 .stream().anyMatch(resolveInfo -> 183 resolveInfo.handleAllWebDataURI 184 && (includeFallback || resolveInfo.priority >= 0)); 185 } 186 startActivity(Intent onlyBrowserIntent)187 private static void startActivity(Intent onlyBrowserIntent) { 188 InstrumentationRegistry.getInstrumentation().getContext().startActivity( 189 onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 190 } 191 setDomainUserSelectionApproval(boolean approved)192 private static void setDomainUserSelectionApproval(boolean approved) { 193 String template = "pm set-app-links-user-selection --package %s --user all %b all"; 194 ShellUtils.runShellCommand(template, SHARED_PKG_NAME, approved); 195 ShellUtils.runShellCommand(template, UNIQUE_AND_SHARED_PKG_NAME, approved); 196 } 197 } 198