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