1 /* 2 * Copyright (C) 2022 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.appenumeration.cts; 18 19 import static android.appenumeration.cts.Constants.ACCOUNT_NAME; 20 import static android.appenumeration.cts.Constants.ACCOUNT_TYPE; 21 import static android.appenumeration.cts.Constants.ACCOUNT_TYPE_SHARED_USER; 22 import static android.appenumeration.cts.Constants.ACTION_GET_IS_SYNCABLE; 23 import static android.appenumeration.cts.Constants.ACTION_GET_PERIODIC_SYNCS; 24 import static android.appenumeration.cts.Constants.ACTION_GET_SYNCADAPTER_CONTROL_PANEL; 25 import static android.appenumeration.cts.Constants.ACTION_GET_SYNCADAPTER_PACKAGES_FOR_AUTHORITY; 26 import static android.appenumeration.cts.Constants.ACTION_GET_SYNCADAPTER_TYPES; 27 import static android.appenumeration.cts.Constants.ACTION_GET_SYNC_AUTOMATICALLY; 28 import static android.appenumeration.cts.Constants.ACTION_REQUEST_PERIODIC_SYNC; 29 import static android.appenumeration.cts.Constants.ACTION_REQUEST_SYNC_AND_AWAIT_STATUS; 30 import static android.appenumeration.cts.Constants.ACTION_SET_SYNC_AUTOMATICALLY; 31 import static android.appenumeration.cts.Constants.AUTHORITY_SUFFIX; 32 import static android.appenumeration.cts.Constants.EXTRA_ACCOUNT; 33 import static android.appenumeration.cts.Constants.EXTRA_AUTHORITY; 34 import static android.appenumeration.cts.Constants.QUERIES_NOTHING; 35 import static android.appenumeration.cts.Constants.QUERIES_NOTHING_SHARED_USER; 36 import static android.appenumeration.cts.Constants.QUERIES_PACKAGE; 37 import static android.appenumeration.cts.Constants.SERVICE_CLASS_SYNC_ADAPTER; 38 import static android.appenumeration.cts.Constants.TARGET_SYNCADAPTER; 39 import static android.appenumeration.cts.Constants.TARGET_SYNCADAPTER_AUTHORITY; 40 import static android.appenumeration.cts.Constants.TARGET_SYNCADAPTER_SHARED_USER; 41 import static android.appenumeration.cts.Utils.allowTestApiAccess; 42 import static android.appenumeration.cts.Utils.resetTestApiAccess; 43 import static android.content.Intent.EXTRA_COMPONENT_NAME; 44 45 import static org.hamcrest.MatcherAssert.assertThat; 46 import static org.hamcrest.Matchers.hasItemInArray; 47 import static org.hamcrest.Matchers.not; 48 import static org.hamcrest.Matchers.notNullValue; 49 import static org.hamcrest.Matchers.nullValue; 50 import static org.hamcrest.core.Is.is; 51 import static org.junit.Assert.assertThrows; 52 53 import android.accounts.Account; 54 import android.accounts.AccountManager; 55 import android.app.PendingIntent; 56 import android.content.ComponentName; 57 import android.content.Intent; 58 import android.content.PeriodicSync; 59 import android.content.SyncAdapterType; 60 import android.os.Bundle; 61 import android.os.Process; 62 63 import androidx.test.ext.junit.runners.AndroidJUnit4; 64 import androidx.test.filters.FlakyTest; 65 66 import org.junit.AfterClass; 67 import org.junit.BeforeClass; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 71 import java.util.List; 72 73 @RunWith(AndroidJUnit4.class) 74 public class SyncAdapterEnumerationTests extends AppEnumerationTestsBase { 75 76 private static AccountManager sAccountManager; 77 78 private static final Account ACCOUNT_SYNCADAPTER = new Account(ACCOUNT_NAME, ACCOUNT_TYPE); 79 private static final Account ACCOUNT_SYNCADAPTER_SHARED_USER = new Account(ACCOUNT_NAME, 80 ACCOUNT_TYPE_SHARED_USER); 81 82 @BeforeClass setUpAccounts()83 public static void setUpAccounts() { 84 sAccountManager = AccountManager.get(sContext); 85 assertThat(sAccountManager.addAccountExplicitly(ACCOUNT_SYNCADAPTER, 86 null /* password */, null /* userdata */), is(true)); 87 assertThat(sAccountManager.addAccountExplicitly(ACCOUNT_SYNCADAPTER_SHARED_USER, 88 null /* password */, null /* userdata */), is(true)); 89 } 90 91 @AfterClass tearDownAccounts()92 public static void tearDownAccounts() { 93 assertThat(sAccountManager.removeAccountExplicitly(ACCOUNT_SYNCADAPTER), 94 is(true)); 95 assertThat(sAccountManager.removeAccountExplicitly(ACCOUNT_SYNCADAPTER_SHARED_USER), 96 is(true)); 97 } 98 99 @Test queriesPackage_getSyncAdapterTypes_canSeeSyncAdapterTarget()100 public void queriesPackage_getSyncAdapterTypes_canSeeSyncAdapterTarget() throws Exception { 101 assertVisible(QUERIES_PACKAGE, TARGET_SYNCADAPTER, this::getSyncAdapterTypes); 102 } 103 104 @Test queriesNothing_getSyncAdapterTypes_cannotSeeSyncAdapterTarget()105 public void queriesNothing_getSyncAdapterTypes_cannotSeeSyncAdapterTarget() throws Exception { 106 assertNotVisible(QUERIES_NOTHING, TARGET_SYNCADAPTER, this::getSyncAdapterTypes); 107 assertNotVisible(QUERIES_NOTHING, TARGET_SYNCADAPTER_SHARED_USER, 108 this::getSyncAdapterTypes); 109 } 110 111 @FlakyTest(bugId = 328849197) 112 @Test queriesNothingSharedUser_getSyncAdapterTypes_canSeeSyncAdapterSharedUserTarget()113 public void queriesNothingSharedUser_getSyncAdapterTypes_canSeeSyncAdapterSharedUserTarget() 114 throws Exception { 115 assertVisible(QUERIES_NOTHING_SHARED_USER, TARGET_SYNCADAPTER_SHARED_USER, 116 this::getSyncAdapterTypes); 117 } 118 119 @Test queriesPackage_getSyncAdapterPackages_canSeeSyncAdapterTarget()120 public void queriesPackage_getSyncAdapterPackages_canSeeSyncAdapterTarget() throws Exception { 121 try { 122 allowTestApiAccess(QUERIES_PACKAGE); 123 124 assertVisible(QUERIES_PACKAGE, TARGET_SYNCADAPTER, 125 this::getSyncAdapterPackagesForAuthorityAsUser); 126 } finally { 127 resetTestApiAccess(QUERIES_PACKAGE); 128 } 129 } 130 131 @Test queriesNothing_getSyncAdapterPackages_cannotSeeSyncAdapterTarget()132 public void queriesNothing_getSyncAdapterPackages_cannotSeeSyncAdapterTarget() 133 throws Exception { 134 try { 135 allowTestApiAccess(QUERIES_NOTHING); 136 137 assertNotVisible(QUERIES_NOTHING, TARGET_SYNCADAPTER, 138 this::getSyncAdapterPackagesForAuthorityAsUser); 139 assertNotVisible(QUERIES_NOTHING, TARGET_SYNCADAPTER_SHARED_USER, 140 this::getSyncAdapterPackagesForAuthorityAsUser); 141 } finally { 142 resetTestApiAccess(QUERIES_NOTHING); 143 } 144 } 145 146 @Test queriesNothingSharedUser_getSyncAdapterPackages_canSeeSyncAdapterSharedUserTarget()147 public void queriesNothingSharedUser_getSyncAdapterPackages_canSeeSyncAdapterSharedUserTarget() 148 throws Exception { 149 try { 150 allowTestApiAccess(QUERIES_NOTHING_SHARED_USER); 151 152 assertVisible(QUERIES_NOTHING_SHARED_USER, TARGET_SYNCADAPTER_SHARED_USER, 153 this::getSyncAdapterPackagesForAuthorityAsUser); 154 } finally { 155 resetTestApiAccess(QUERIES_NOTHING_SHARED_USER); 156 } 157 } 158 159 @Test queriesPackage_requestSync_canSeeSyncAdapterTarget()160 public void queriesPackage_requestSync_canSeeSyncAdapterTarget() throws Exception { 161 assertThat( 162 requestSyncAndAwaitStatus(QUERIES_PACKAGE, ACCOUNT_SYNCADAPTER, TARGET_SYNCADAPTER), 163 is(true)); 164 } 165 166 @FlakyTest(bugId = 328849197) queriesNothingSharedUser_requestSync_canSeeSyncAdapterSharedUserTarget()167 public void queriesNothingSharedUser_requestSync_canSeeSyncAdapterSharedUserTarget() 168 throws Exception { 169 assertThat(requestSyncAndAwaitStatus(QUERIES_NOTHING_SHARED_USER, 170 ACCOUNT_SYNCADAPTER_SHARED_USER, TARGET_SYNCADAPTER_SHARED_USER), is(true)); 171 } 172 173 @Test queriesNothing_requestSync_cannotSeeSyncAdapterTarget()174 public void queriesNothing_requestSync_cannotSeeSyncAdapterTarget() { 175 assertThrows(MissingCallbackException.class, 176 () -> requestSyncAndAwaitStatus(QUERIES_NOTHING, ACCOUNT_SYNCADAPTER, 177 TARGET_SYNCADAPTER)); 178 assertThrows(MissingCallbackException.class, 179 () -> requestSyncAndAwaitStatus(QUERIES_NOTHING, ACCOUNT_SYNCADAPTER_SHARED_USER, 180 TARGET_SYNCADAPTER_SHARED_USER)); 181 } 182 183 @Test queriesPackage_getRunningServiceControlPanel_canSeeSyncAdapterTarget()184 public void queriesPackage_getRunningServiceControlPanel_canSeeSyncAdapterTarget() 185 throws Exception { 186 assertThat(getSyncAdapterControlPanel(QUERIES_PACKAGE, ACCOUNT_SYNCADAPTER, 187 TARGET_SYNCADAPTER), notNullValue()); 188 } 189 190 @Test queriesNothing_getRunningServiceControlPanel_cannotSeeSyncAdapterTarget()191 public void queriesNothing_getRunningServiceControlPanel_cannotSeeSyncAdapterTarget() 192 throws Exception { 193 assertThat(getSyncAdapterControlPanel(QUERIES_NOTHING, ACCOUNT_SYNCADAPTER, 194 TARGET_SYNCADAPTER), nullValue()); 195 } 196 197 @Test queriesNothing_getIsSyncable_cannotSeeSyncAdapterTarget()198 public void queriesNothing_getIsSyncable_cannotSeeSyncAdapterTarget() throws Exception { 199 assertThat(getIsSyncable(QUERIES_NOTHING, ACCOUNT_SYNCADAPTER, TARGET_SYNCADAPTER), 200 is(0)); 201 } 202 203 @Test queriesPackage_getIsSyncable_canSeeSyncAdapterTarget()204 public void queriesPackage_getIsSyncable_canSeeSyncAdapterTarget() throws Exception { 205 assertThat(getIsSyncable(QUERIES_PACKAGE, ACCOUNT_SYNCADAPTER, TARGET_SYNCADAPTER), 206 not(is(0))); 207 } 208 209 @Test queriesNothing_getSyncAutomatically_cannotSeeSyncAdapterTarget()210 public void queriesNothing_getSyncAutomatically_cannotSeeSyncAdapterTarget() throws Exception { 211 setSyncAutomatically(TARGET_SYNCADAPTER, ACCOUNT_SYNCADAPTER); 212 assertThat(getSyncAutomatically(QUERIES_NOTHING, TARGET_SYNCADAPTER), is(false)); 213 } 214 215 @Test queriesPackage_getSyncAutomatically_canSeeSyncAdapterTarget()216 public void queriesPackage_getSyncAutomatically_canSeeSyncAdapterTarget() throws Exception { 217 setSyncAutomatically(TARGET_SYNCADAPTER, ACCOUNT_SYNCADAPTER); 218 assertThat(getSyncAutomatically(QUERIES_PACKAGE, TARGET_SYNCADAPTER), is(true)); 219 } 220 221 @Test queriesNothing_getPeriodicSyncs_cannotSeeSyncAdapterTarget()222 public void queriesNothing_getPeriodicSyncs_cannotSeeSyncAdapterTarget() throws Exception { 223 assertThat(requestPeriodicSync(TARGET_SYNCADAPTER, ACCOUNT_SYNCADAPTER), is(true)); 224 assertThat(getPeriodicSyncs(QUERIES_NOTHING, ACCOUNT_SYNCADAPTER, TARGET_SYNCADAPTER), 225 not(hasItemInArray(TARGET_SYNCADAPTER_AUTHORITY))); 226 } 227 228 @Test queriesPackage_getPeriodicSyncs_canSeeSyncAdapterTarget()229 public void queriesPackage_getPeriodicSyncs_canSeeSyncAdapterTarget() throws Exception { 230 assertThat(requestPeriodicSync(TARGET_SYNCADAPTER, ACCOUNT_SYNCADAPTER), is(true)); 231 assertThat(getPeriodicSyncs(QUERIES_PACKAGE, ACCOUNT_SYNCADAPTER, TARGET_SYNCADAPTER), 232 hasItemInArray(TARGET_SYNCADAPTER_AUTHORITY)); 233 } 234 235 getSyncAdapterTypes(String sourcePackageName)236 private String[] getSyncAdapterTypes(String sourcePackageName) throws Exception { 237 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 238 /* intentExtra */ null, ACTION_GET_SYNCADAPTER_TYPES); 239 final List<SyncAdapterType> types = response.getParcelableArrayList( 240 Intent.EXTRA_RETURN_RESULT, SyncAdapterType.class); 241 return types.stream() 242 .map(type -> type.getPackageName()) 243 .distinct() 244 .toArray(String[]::new); 245 } 246 getSyncAdapterPackagesForAuthorityAsUser(String sourcePackageName, String targetPackageName)247 private String[] getSyncAdapterPackagesForAuthorityAsUser(String sourcePackageName, 248 String targetPackageName) throws Exception { 249 final Bundle extraData = new Bundle(); 250 extraData.putString(EXTRA_AUTHORITY, targetPackageName + AUTHORITY_SUFFIX); 251 extraData.putInt(Intent.EXTRA_USER, Process.myUserHandle().getIdentifier()); 252 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 253 extraData, ACTION_GET_SYNCADAPTER_PACKAGES_FOR_AUTHORITY); 254 return response.getStringArray(Intent.EXTRA_PACKAGES); 255 } 256 requestSyncAndAwaitStatus(String sourcePackageName, Account account, String targetPackageName)257 private boolean requestSyncAndAwaitStatus(String sourcePackageName, Account account, 258 String targetPackageName) throws Exception { 259 final Bundle extraData = new Bundle(); 260 extraData.putParcelable(EXTRA_ACCOUNT, account); 261 extraData.putString(EXTRA_AUTHORITY, targetPackageName + AUTHORITY_SUFFIX); 262 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 263 extraData, ACTION_REQUEST_SYNC_AND_AWAIT_STATUS); 264 return response.getBoolean(Intent.EXTRA_RETURN_RESULT); 265 } 266 getSyncAdapterControlPanel(String sourcePackageName, Account account, String targetPackageName)267 private PendingIntent getSyncAdapterControlPanel(String sourcePackageName, Account account, 268 String targetPackageName) throws Exception { 269 final ComponentName componentName = new ComponentName( 270 targetPackageName, SERVICE_CLASS_SYNC_ADAPTER); 271 final Bundle extraData = new Bundle(); 272 extraData.putParcelable(EXTRA_ACCOUNT, account); 273 extraData.putString(EXTRA_AUTHORITY, targetPackageName + AUTHORITY_SUFFIX); 274 extraData.putParcelable(EXTRA_COMPONENT_NAME, componentName); 275 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 276 extraData, ACTION_GET_SYNCADAPTER_CONTROL_PANEL); 277 return response.getParcelable(Intent.EXTRA_RETURN_RESULT, PendingIntent.class); 278 } 279 requestPeriodicSync(String providerPackageName, Account account)280 private boolean requestPeriodicSync(String providerPackageName, Account account) 281 throws Exception { 282 final String authority = providerPackageName + AUTHORITY_SUFFIX; 283 final Bundle extraData = new Bundle(); 284 extraData.putParcelable(EXTRA_ACCOUNT, account); 285 extraData.putString(EXTRA_AUTHORITY, authority); 286 final Bundle response = sendCommandBlocking(providerPackageName, 287 null /* targetPackageName */, extraData, ACTION_REQUEST_PERIODIC_SYNC); 288 return response.getBoolean(Intent.EXTRA_RETURN_RESULT); 289 } 290 setSyncAutomatically(String providerPackageName, Account account)291 private void setSyncAutomatically(String providerPackageName, Account account) 292 throws Exception { 293 final String authority = providerPackageName + AUTHORITY_SUFFIX; 294 final Bundle extraData = new Bundle(); 295 extraData.putParcelable(EXTRA_ACCOUNT, account); 296 extraData.putString(EXTRA_AUTHORITY, authority); 297 sendCommandBlocking(providerPackageName, null /* targetPackageName */, extraData, 298 ACTION_SET_SYNC_AUTOMATICALLY); 299 } 300 getSyncAutomatically(String sourcePackageName, String targetPackageName)301 private boolean getSyncAutomatically(String sourcePackageName, String targetPackageName) 302 throws Exception { 303 final String authority = targetPackageName + AUTHORITY_SUFFIX; 304 final Bundle extraData = new Bundle(); 305 extraData.putString(EXTRA_AUTHORITY, authority); 306 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 307 extraData, ACTION_GET_SYNC_AUTOMATICALLY); 308 return response.getBoolean(Intent.EXTRA_RETURN_RESULT); 309 } 310 getIsSyncable(String sourcePackageName, Account account, String targetPackageName)311 private int getIsSyncable(String sourcePackageName, Account account, String targetPackageName) 312 throws Exception { 313 final String authority = targetPackageName + AUTHORITY_SUFFIX; 314 final Bundle extraData = new Bundle(); 315 extraData.putParcelable(EXTRA_ACCOUNT, account); 316 extraData.putString(EXTRA_AUTHORITY, authority); 317 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 318 extraData, ACTION_GET_IS_SYNCABLE); 319 return response.getInt(Intent.EXTRA_RETURN_RESULT); 320 } 321 getPeriodicSyncs(String sourcePackageName, Account account, String targetPackageName)322 private String[] getPeriodicSyncs(String sourcePackageName, Account account, 323 String targetPackageName) throws Exception { 324 final String authority = targetPackageName + AUTHORITY_SUFFIX; 325 final Bundle extraData = new Bundle(); 326 extraData.putParcelable(EXTRA_ACCOUNT, account); 327 extraData.putString(EXTRA_AUTHORITY, authority); 328 final Bundle response = sendCommandBlocking(sourcePackageName, null /* targetPackageName */, 329 extraData, ACTION_GET_PERIODIC_SYNCS); 330 final List<PeriodicSync> list = response.getParcelableArrayList(Intent.EXTRA_RETURN_RESULT, 331 PeriodicSync.class); 332 return list.stream() 333 .map(sync -> sync.authority) 334 .distinct() 335 .toArray(String[]::new); 336 } 337 } 338