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 assumeFalse("Skipping test for car", FeatureUtil.isAutomotive()); 127 setDomainUserSelectionApproval(false); 128 startNoBrowserRequireDefaultInternal(false); 129 } 130 startNoBrowserRequireDefaultInternal(boolean isDomainApproved)131 private void startNoBrowserRequireDefaultInternal(boolean isDomainApproved) { 132 Intent uniqueUriIntent = new Intent(Intent.ACTION_VIEW) 133 .addCategory(Intent.CATEGORY_BROWSABLE) 134 .setData(Uri.parse(UNIQUE_URI)); 135 136 startActivity(uniqueUriIntent); 137 138 Intent uniqueUriIntentNoBrowserRequireDefault = new Intent(uniqueUriIntent) 139 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER) 140 .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT); 141 142 if (isBrowserPresent(false)) { 143 // with non-browser, we'd expect the resolver 144 // with require default, we'll get activity not found 145 try { 146 startActivity(uniqueUriIntentNoBrowserRequireDefault); 147 if (!isDomainApproved) { 148 fail("Should fail to launch when started with non-browser and require default" 149 + " when browser present"); 150 } 151 } catch (ActivityNotFoundException e) { 152 // hooray! 153 if (isDomainApproved) { 154 // Domain approval should force only the test Activity to be returned, which 155 // means it should pass the above flags and launch. 156 fail("Should succeed launch when started with non-browser and require default" 157 + " when browser present"); 158 } 159 } 160 } else { 161 // with non-browser, but no browser present, we'd get a single result 162 // with require default, we'll resolve to that single result 163 try { 164 startActivity(uniqueUriIntentNoBrowserRequireDefault); 165 if (!isDomainApproved) { 166 fail("Should fail to launch when started with non-browser and require default" 167 + " when browser not present"); 168 } 169 } catch (ActivityNotFoundException e) { 170 if (isDomainApproved) { 171 fail("Should succeed launch when started with non-browser and require default" 172 + " when browser not present"); 173 } 174 } 175 } 176 } 177 isBrowserPresent(boolean includeFallback)178 private static boolean isBrowserPresent(boolean includeFallback) { 179 return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager() 180 .queryIntentActivities(new Intent(Intent.ACTION_VIEW).addCategory( 181 Intent.CATEGORY_BROWSABLE).setData(Uri.parse(ONLY_BROWSER_URI)), 182 0 /* flags */) 183 .stream().anyMatch(resolveInfo -> 184 resolveInfo.handleAllWebDataURI 185 && (includeFallback || resolveInfo.priority >= 0)); 186 } 187 startActivity(Intent onlyBrowserIntent)188 private static void startActivity(Intent onlyBrowserIntent) { 189 InstrumentationRegistry.getInstrumentation().getContext().startActivity( 190 onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 191 } 192 setDomainUserSelectionApproval(boolean approved)193 private static void setDomainUserSelectionApproval(boolean approved) { 194 String template = "pm set-app-links-user-selection --package %s --user all %b all"; 195 ShellUtils.runShellCommand(template, SHARED_PKG_NAME, approved); 196 ShellUtils.runShellCommand(template, UNIQUE_AND_SHARED_PKG_NAME, approved); 197 } 198 } 199