1 /*
2  * Copyright (C) 2016 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 com.android.cts.content;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.app.Activity;
22 import android.content.ContentProviderClient;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SyncRequest;
27 import android.content.SyncResult;
28 import android.content.cts.FlakyTestRule;
29 import android.content.pm.PackageManager;
30 import android.net.ConnectivityManager;
31 import android.net.NetworkInfo;
32 import android.os.Bundle;
33 import android.os.Process;
34 import android.os.SystemClock;
35 import android.support.test.InstrumentationRegistry;
36 import android.support.test.runner.AndroidJUnit4;
37 import android.support.test.uiautomator.By;
38 import android.support.test.uiautomator.UiDevice;
39 import android.support.test.uiautomator.Until;
40 import org.junit.After;
41 import org.junit.Before;
42 import org.junit.Rule;
43 import org.junit.Test;
44 import org.junit.rules.TestRule;
45 import org.junit.runner.RunWith;
46 
47 import java.io.IOException;
48 import java.util.concurrent.CountDownLatch;
49 import java.util.concurrent.TimeUnit;
50 
51 import static junit.framework.Assert.assertFalse;
52 import static junit.framework.Assert.assertTrue;
53 
54 import com.android.compatibility.common.util.SystemUtil;
55 
56 /**
57  * Tests whether a sync adapter can access accounts.
58  */
59 @RunWith(AndroidJUnit4.class)
60 public class CtsSyncAccountAccessOtherCertTestCases {
61     private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
62     private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
63 
64     public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
65 
66     @Rule
67     public final TestRule mFlakyTestRule = new FlakyTestRule(3);
68 
69     @Before
setUp()70     public void setUp() throws Exception {
71         allowSyncAdapterRunInBackgroundAndDataInBackground();
72     }
73 
74     @After
tearDown()75     public void tearDown() throws Exception {
76         disallowSyncAdapterRunInBackgroundAndDataInBackground();
77     }
78 
79     @Test
testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount()80     public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
81         if (!hasDataConnection() || !hasNotificationSupport()) {
82             return;
83         }
84 
85         Intent intent = new Intent(getContext(), StubActivity.class);
86         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
87 
88         AccountManager accountManager = getContext().getSystemService(AccountManager.class);
89         Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
90                 null, null).getResult();
91 
92         Account addedAccount = new Account(
93                 result.getString(AccountManager.KEY_ACCOUNT_NAME),
94                 result.getString(AccountManager.KEY_ACCOUNT_TYPE));
95 
96         waitForSyncManagerAccountChangeUpdate();
97 
98         try {
99             CountDownLatch latch = new CountDownLatch(1);
100 
101             SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
102                     String authority, ContentProviderClient provider, SyncResult syncResult)
103                     -> latch.countDown());
104 
105             Bundle extras = new Bundle();
106             extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
107             extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
108             extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
109             SyncRequest request = new SyncRequest.Builder()
110                     .setSyncAdapter(null, "com.android.cts.stub.provider")
111                     .syncOnce()
112                     .setExtras(extras)
113                     .setExpedited(true)
114                     .setManual(true)
115                     .build();
116             ContentResolver.requestSync(request);
117 
118             assertFalse(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
119 
120             UiDevice uiDevice = getUiDevice();
121 
122             uiDevice.openNotification();
123             uiDevice.wait(Until.hasObject(By.text("Permission requested")),
124                     UI_TIMEOUT_MILLIS);
125 
126             uiDevice.findObject(By.text("Permission requested")).click();
127             uiDevice.wait(Until.hasObject(By.text("ALLOW")),
128                     UI_TIMEOUT_MILLIS);
129 
130             uiDevice.findObject(By.text("ALLOW")).click();
131 
132             ContentResolver.requestSync(request);
133 
134             assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
135         } finally {
136             // Ask the differently signed authenticator to drop all accounts
137             accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
138                     null, false, null, null);
139         }
140     }
141 
getContext()142     private Context getContext() {
143         return InstrumentationRegistry.getInstrumentation().getContext();
144     }
145 
getUiDevice()146     private UiDevice getUiDevice() {
147         return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
148     }
149 
waitForSyncManagerAccountChangeUpdate()150     private void waitForSyncManagerAccountChangeUpdate() {
151         // Wait for the sync manager to be notified for the new account.
152         // Unfortunately, there is no way to detect this event, sigh...
153         SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
154     }
155 
hasDataConnection()156     private boolean hasDataConnection() {
157         ConnectivityManager connectivityManager = getContext().getSystemService(
158                 ConnectivityManager.class);
159         NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
160         return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
161     }
162 
hasNotificationSupport()163     private boolean hasNotificationSupport() {
164         return !getContext().getPackageManager()
165                 .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
166     }
167 
allowSyncAdapterRunInBackgroundAndDataInBackground()168     private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
169         // Allow us to run in the background
170         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
171                 "cmd deviceidle whitelist +" + getContext().getPackageName());
172         // Allow us to use data in the background
173         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
174                 "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
175     }
176 
disallowSyncAdapterRunInBackgroundAndDataInBackground()177     private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
178         // Allow us to run in the background
179         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
180                 "cmd deviceidle whitelist -" + getContext().getPackageName());
181         // Allow us to use data in the background
182         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
183                 "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
184     }
185 }
186