1 /* 2 * Copyright (C) 2018 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.permission.cts; 18 19 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 20 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; 21 import static android.content.pm.PackageManager.GET_PERMISSIONS; 22 23 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.fail; 27 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageInfo; 32 import android.content.pm.PackageManager; 33 import android.content.res.Resources; 34 import android.platform.test.annotations.AppModeFull; 35 import android.platform.test.annotations.AsbSecurityTest; 36 import android.support.test.uiautomator.By; 37 import android.support.test.uiautomator.UiDevice; 38 import android.support.test.uiautomator.UiScrollable; 39 import android.support.test.uiautomator.UiSelector; 40 import android.widget.ScrollView; 41 42 import androidx.test.InstrumentationRegistry; 43 44 import com.android.compatibility.common.util.SystemUtil; 45 46 import org.junit.After; 47 import org.junit.Before; 48 import org.junit.Test; 49 50 import java.util.concurrent.TimeUnit; 51 import java.util.regex.Pattern; 52 53 public class PermissionGroupChange { 54 private static final String APP_PKG_NAME = "android.permission.cts.appthatrequestpermission"; 55 private static final long EXPECTED_BEHAVIOR_TIMEOUT_SEC = 15; 56 private static final long UNEXPECTED_BEHAVIOR_TIMEOUT_SEC = 2; 57 58 private Context mContext; 59 private UiDevice mUiDevice; 60 private String mAllowButtonText = null; 61 62 @Before setContextAndUiDevice()63 public void setContextAndUiDevice() { 64 mContext = InstrumentationRegistry.getTargetContext(); 65 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 66 } 67 68 @Before wakeUpScreen()69 public void wakeUpScreen() { 70 SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP"); 71 } 72 73 /** 74 * Retry for a time until the runnable stops throwing. 75 * 76 * @param runnable The condition to execute 77 * @param timeoutSec The time to try 78 */ eventually(ThrowingRunnable runnable, long timeoutSec)79 private void eventually(ThrowingRunnable runnable, long timeoutSec) throws Throwable { 80 long startTime = System.nanoTime(); 81 while (true) { 82 try { 83 runnable.run(); 84 return; 85 } catch (Throwable t) { 86 if (System.nanoTime() - startTime < TimeUnit.SECONDS.toNanos(timeoutSec)) { 87 Thread.sleep(100); 88 continue; 89 } 90 91 throw t; 92 } 93 } 94 } 95 96 scrollToBottomIfWatch()97 private void scrollToBottomIfWatch() throws Exception { 98 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 99 UiScrollable scrollable = new UiScrollable( 100 new UiSelector().className(ScrollView.class)); 101 if (scrollable.exists()) { 102 scrollable.flingToEnd(10); 103 } 104 } 105 } 106 clickAllowButton()107 protected void clickAllowButton() throws Exception { 108 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 109 if (mAllowButtonText == null) { 110 mAllowButtonText = getPermissionControllerString("grant_dialog_button_allow"); 111 } 112 mUiDevice.findObject(By.text(Pattern.compile(Pattern.quote(mAllowButtonText), 113 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE))).click(); 114 } else { 115 mUiDevice.findObject(By.res( 116 "com.android.permissioncontroller:id/permission_allow_button")).click(); 117 } 118 } 119 grantPermissionViaUi()120 private void grantPermissionViaUi() throws Throwable { 121 eventually(() -> { 122 scrollToBottomIfWatch(); 123 clickAllowButton(); 124 }, EXPECTED_BEHAVIOR_TIMEOUT_SEC); 125 } 126 waitUntilPermissionGranted(String permName, long timeoutSec)127 private void waitUntilPermissionGranted(String permName, long timeoutSec) throws Throwable { 128 eventually(() -> { 129 PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(APP_PKG_NAME, 130 GET_PERMISSIONS); 131 132 for (int i = 0; i < appInfo.requestedPermissions.length; i++) { 133 if (appInfo.requestedPermissions[i].equals(permName) 134 && ((appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) 135 != 0)) { 136 return; 137 } 138 } 139 140 fail(permName + " not granted"); 141 }, timeoutSec); 142 } 143 installApp(String apk)144 private void installApp(String apk) { 145 String installResult = SystemUtil.runShellCommand( 146 "pm install -r data/local/tmp/cts/permissions/" + apk + ".apk"); 147 assertEquals("Success", installResult.trim()); 148 } 149 150 /** 151 * Start the app. The app will request the permissions. 152 */ startApp()153 private void startApp() { 154 Intent startApp = new Intent(); 155 startApp.setComponent(new ComponentName(APP_PKG_NAME, APP_PKG_NAME + ".RequestPermission")); 156 startApp.setFlags(FLAG_ACTIVITY_NEW_TASK); 157 158 mContext.startActivity(startApp); 159 } 160 161 @After uninstallTestApp()162 public void uninstallTestApp() { 163 runShellCommand("pm uninstall android.permission.cts.appthatrequestpermission"); 164 } 165 166 @Test 167 @AppModeFull 168 @AsbSecurityTest(cveBugId = 72710897) permissionGroupShouldNotBeAutoGrantedIfNewMember()169 public void permissionGroupShouldNotBeAutoGrantedIfNewMember() throws Throwable { 170 installApp("CtsAppThatRequestsPermissionAandB"); 171 172 startApp(); 173 grantPermissionViaUi(); 174 waitUntilPermissionGranted("android.permission.cts.appthatrequestpermission.A", 175 EXPECTED_BEHAVIOR_TIMEOUT_SEC); 176 177 // Update app which changes the permission group of "android.permission.cts 178 // .appthatrequestpermission.A" to the same as "android.permission.cts.C" 179 installApp("CtsAppThatRequestsPermissionAandC"); 180 181 startApp(); 182 try { 183 // The permission should not be auto-granted 184 waitUntilPermissionGranted("android.permission.cts.C", UNEXPECTED_BEHAVIOR_TIMEOUT_SEC); 185 fail("android.permission.cts.C was auto-granted"); 186 } catch (Throwable expected) { 187 assertEquals("android.permission.cts.C not granted", expected.getMessage()); 188 } 189 } 190 getPermissionControllerString(String res)191 private String getPermissionControllerString(String res) 192 throws PackageManager.NameNotFoundException { 193 Resources permissionControllerResources = mContext.createPackageContext( 194 mContext.getPackageManager().getPermissionControllerPackageName(), 0) 195 .getResources(); 196 return permissionControllerResources.getString(permissionControllerResources 197 .getIdentifier(res, "string", "com.android.permissioncontroller")); 198 } 199 200 private interface ThrowingRunnable { run()201 void run() throws Throwable; 202 } 203 } 204