Home | History | Annotate | Download | only in content
      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 static com.android.cts.content.Utils.ALWAYS_SYNCABLE_AUTHORITY;
     20 import static com.android.cts.content.Utils.SYNC_TIMEOUT_MILLIS;
     21 import static com.android.cts.content.Utils.allowSyncAdapterRunInBackgroundAndDataInBackground;
     22 import static com.android.cts.content.Utils.disallowSyncAdapterRunInBackgroundAndDataInBackground;
     23 import static com.android.cts.content.Utils.getUiDevice;
     24 import static com.android.cts.content.Utils.hasDataConnection;
     25 import static com.android.cts.content.Utils.hasNotificationSupport;
     26 import static com.android.cts.content.Utils.isWatch;
     27 import static com.android.cts.content.Utils.requestSync;
     28 import static com.android.cts.content.Utils.withAccount;
     29 
     30 import static org.junit.Assume.assumeFalse;
     31 import static org.junit.Assume.assumeTrue;
     32 import static org.mockito.ArgumentMatchers.any;
     33 import static org.mockito.Mockito.never;
     34 import static org.mockito.Mockito.timeout;
     35 import static org.mockito.Mockito.verify;
     36 
     37 import android.app.ActivityManager;
     38 import android.content.AbstractThreadedSyncAdapter;
     39 import android.content.ContentResolver;
     40 import android.content.Context;
     41 import android.content.SyncRequest;
     42 import android.content.res.Configuration;
     43 import android.support.test.uiautomator.By;
     44 import android.support.test.uiautomator.UiDevice;
     45 import android.support.test.uiautomator.UiObject2;
     46 import android.support.test.uiautomator.Until;
     47 import android.util.Log;
     48 
     49 import androidx.test.InstrumentationRegistry;
     50 import androidx.test.rule.ActivityTestRule;
     51 import androidx.test.runner.AndroidJUnit4;
     52 
     53 import org.junit.After;
     54 import org.junit.Before;
     55 import org.junit.Rule;
     56 import org.junit.Test;
     57 import org.junit.rules.TestRule;
     58 import org.junit.runner.RunWith;
     59 
     60 import java.io.ByteArrayOutputStream;
     61 import java.util.regex.Pattern;
     62 
     63 /**
     64  * Tests whether a sync adapter can access accounts.
     65  */
     66 @RunWith(AndroidJUnit4.class)
     67 public class CtsSyncAccountAccessOtherCertTestCases {
     68     private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
     69     private static final String LOG_TAG =
     70             CtsSyncAccountAccessOtherCertTestCases.class.getSimpleName();
     71 
     72     private static final Pattern PERMISSION_REQUESTED = Pattern.compile(
     73             "Permission Requested|Permission requested");
     74     private static final Pattern ALLOW_SYNC = Pattern.compile("ALLOW|Allow");
     75     private static final String OPEN_NOTIFICATION_WATCH = "Open";
     76 
     77     @Rule
     78     public final TestRule mFlakyTestRule = new FlakyTestRule(3);
     79 
     80     @Rule
     81     public final ActivityTestRule<StubActivity> activity = new ActivityTestRule(StubActivity.class);
     82 
     83     @Before
     84     public void setUp() throws Exception {
     85         allowSyncAdapterRunInBackgroundAndDataInBackground();
     86     }
     87 
     88     @After
     89     public void tearDown() throws Exception {
     90         disallowSyncAdapterRunInBackgroundAndDataInBackground();
     91     }
     92 
     93     @Test
     94     public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
     95         assumeTrue(hasDataConnection());
     96         assumeTrue(hasNotificationSupport());
     97         assumeFalse(isRunningInVR());
     98 
     99         // If running in a test harness the Account Manager never denies access to an account. Hence
    100         // the permission request will not trigger. b/72114924
    101         assumeFalse(ActivityManager.isRunningInTestHarness());
    102 
    103         try (AutoCloseable ignored = withAccount(activity.getActivity())) {
    104             AbstractThreadedSyncAdapter adapter = AlwaysSyncableSyncService.getInstance(
    105                     activity.getActivity()).setNewDelegate();
    106 
    107             SyncRequest request = requestSync(ALWAYS_SYNCABLE_AUTHORITY);
    108             Log.i(LOG_TAG, "Sync requested " + request);
    109 
    110             Thread.sleep(SYNC_TIMEOUT_MILLIS);
    111             verify(adapter, never()).onPerformSync(any(), any(), any(), any(), any());
    112             Log.i(LOG_TAG, "Did not get onPerformSync");
    113 
    114             UiDevice uiDevice = getUiDevice();
    115             if (isWatch()) {
    116                 UiObject2 notification = findPermissionNotificationInStream(uiDevice);
    117                 notification.click();
    118                 UiObject2 openButton = uiDevice.wait(
    119                         Until.findObject(By.text(OPEN_NOTIFICATION_WATCH)), UI_TIMEOUT_MILLIS);
    120                 if (openButton != null) {
    121                     // older sysui may not have the "open" button
    122                     openButton.click();
    123                 }
    124             } else {
    125                 uiDevice.openNotification();
    126                 try {
    127                     UiObject2 permissionRequest = uiDevice.wait(
    128                             Until.findObject(By.text(PERMISSION_REQUESTED)), UI_TIMEOUT_MILLIS);
    129 
    130                     permissionRequest.click();
    131                 } catch (Throwable t) {
    132                     ByteArrayOutputStream os = new ByteArrayOutputStream();
    133                     getUiDevice().dumpWindowHierarchy(os);
    134 
    135                     Log.w(LOG_TAG, "Window hierarchy:");
    136                     for (String line : os.toString("UTF-8").split("\n")) {
    137                         Log.w(LOG_TAG, line);
    138 
    139                         // Do not overwhelm logging
    140                         Thread.sleep(10);
    141                     }
    142 
    143                     throw  t;
    144                 }
    145             }
    146 
    147             uiDevice.wait(Until.findObject(By.text(ALLOW_SYNC)), UI_TIMEOUT_MILLIS).click();
    148 
    149             ContentResolver.requestSync(request);
    150 
    151             verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
    152                     any());
    153             Log.i(LOG_TAG, "Got onPerformSync");
    154         }
    155     }
    156 
    157     private UiObject2 findPermissionNotificationInStream(UiDevice uiDevice) {
    158         uiDevice.pressHome();
    159         swipeUp(uiDevice);
    160         if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
    161           return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
    162         }
    163         for (int i = 0; i < 100; i++) {
    164           if (!swipeUp(uiDevice)) {
    165             // We have reached the end of the stream and not found the target.
    166             break;
    167           }
    168           if (uiDevice.hasObject(By.text(PERMISSION_REQUESTED))) {
    169             return uiDevice.findObject(By.text(PERMISSION_REQUESTED));
    170           }
    171         }
    172         return null;
    173     }
    174 
    175     private boolean swipeUp(UiDevice uiDevice) {
    176         int width = uiDevice.getDisplayWidth();
    177         int height = uiDevice.getDisplayHeight();
    178         return uiDevice.swipe(
    179             width / 2 /* startX */,
    180             height - 1 /* startY */,
    181             width / 2 /* endX */,
    182             1 /* endY */,
    183             50 /* numberOfSteps */);
    184     }
    185 
    186     private boolean isRunningInVR() {
    187         final Context context = InstrumentationRegistry.getTargetContext();
    188         return ((context.getResources().getConfiguration().uiMode &
    189                  Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_VR_HEADSET);
    190     }
    191 }
    192