1 /*
2  * Copyright (C) 2021 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.translation.cts;
18 
19 import android.content.ComponentName;
20 import android.service.contentcapture.ActivityEvent;
21 import android.service.contentcapture.ContentCaptureService;
22 import android.service.contentcapture.DataShareCallback;
23 import android.util.ArraySet;
24 import android.util.Log;
25 import android.util.Pair;
26 import android.view.contentcapture.ContentCaptureContext;
27 import android.view.contentcapture.ContentCaptureEvent;
28 import android.view.contentcapture.ContentCaptureSessionId;
29 import android.view.contentcapture.DataRemovalRequest;
30 import android.view.contentcapture.DataShareRequest;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 
35 import java.util.Set;
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.TimeUnit;
38 
39 /**
40  * Implementation of {@link ContentCaptureService} used in CTS tests.
41  */
42 public class CtsContentCaptureService extends ContentCaptureService {
43 
44     private static final String TAG = "CtsContentCaptureService";
45 
46     public static final String SERVICE_PACKAGE = "android.translation.cts";
47     public static final String SERVICE_NAME = SERVICE_PACKAGE + "/."
48             + CtsContentCaptureService.class.getSimpleName();
49 
50     public static final long GENERIC_TIMEOUT_MS = 10_000;
51 
52     private static ServiceWatcher sServiceWatcher;
53 
54     private ContentCaptureContext mContentCaptureContext;
55     private final CountDownLatch mSessionCreatedLatch = new CountDownLatch(1);
56 
57     @Override
onConnected()58     public void onConnected() {
59         Log.i(TAG, "onConnected.");
60 
61         if (sServiceWatcher != null) {
62             sServiceWatcher.mService = this;
63             sServiceWatcher.mConnected.countDown();
64         }
65     }
66 
67     @Override
onDisconnected()68     public void onDisconnected() {
69         Log.i(TAG, "onDisconnected.");
70         if (sServiceWatcher != null) {
71             sServiceWatcher.mService = null;
72             sServiceWatcher.mDisconnected.countDown();
73             sServiceWatcher = null;
74         }
75     }
76 
77     @Override
onCreateContentCaptureSession(ContentCaptureContext context, ContentCaptureSessionId sessionId)78     public void onCreateContentCaptureSession(ContentCaptureContext context,
79             ContentCaptureSessionId sessionId) {
80         Log.i(TAG, "onCreateContentCaptureSession.");
81         mSessionCreatedLatch.countDown();
82         mContentCaptureContext = context;
83     }
84 
85     @Override
onDestroyContentCaptureSession(ContentCaptureSessionId sessionId)86     public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
87         Log.i(TAG, "onDestroyContentCaptureSession.");
88     }
89 
90     @Override
onContentCaptureEvent(ContentCaptureSessionId sessionId, ContentCaptureEvent event)91     public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
92             ContentCaptureEvent event) {
93 
94     }
95 
96     @Override
onDataRemovalRequest(DataRemovalRequest request)97     public void onDataRemovalRequest(DataRemovalRequest request) {
98 
99     }
100 
101     @Override
onDataShareRequest(DataShareRequest request, DataShareCallback callback)102     public void onDataShareRequest(DataShareRequest request, DataShareCallback callback) {
103 
104     }
105 
106     @Override
onActivityEvent(ActivityEvent event)107     public void onActivityEvent(ActivityEvent event) {
108 
109     }
110 
111     /**
112      * Set the ServiceWatcher that used to monitor the service status.
113      */
setServiceWatcher()114     public static ServiceWatcher setServiceWatcher() {
115         if (sServiceWatcher != null) {
116             throw new IllegalStateException("There Can Be Only One!");
117         }
118         sServiceWatcher = new ServiceWatcher();
119         return sServiceWatcher;
120     }
121 
122     /**
123      * Resets the static state of this Service. Called before each test.
124      */
resetStaticState()125     public static void resetStaticState() {
126         sServiceWatcher = null;
127     }
128 
129     /**
130      * Get the ContentCaptureContext that set by {@link #onCreateContentCaptureSession}.
131      */
getContentCaptureContext()132     ContentCaptureContext getContentCaptureContext() {
133         return mContentCaptureContext;
134     }
135 
136     /**
137      * Wait the ContentCapture session created.
138      */
awaitSessionCreated(long timeoutMillis)139     void awaitSessionCreated(long timeoutMillis) {
140         try {
141             mSessionCreatedLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
142         } catch (InterruptedException e) {
143             // do nothing
144         }
145     }
146 
147     /**
148      * Helper class to monitor the state of the service.
149      */
150     public static final class ServiceWatcher {
151         private final CountDownLatch mConnected = new CountDownLatch(1);
152         private final CountDownLatch mDisconnected = new CountDownLatch(1);
153 
154         CtsContentCaptureService mService;
155         private Pair<Set<String>, Set<ComponentName>> mAllowList;
156 
157         @NonNull
waitOnConnected()158         public CtsContentCaptureService waitOnConnected() throws InterruptedException {
159             await(mConnected, "not connected");
160 
161             if (mService == null) {
162                 throw new IllegalStateException("not connected");
163             }
164             if (mAllowList != null) {
165                 Log.d(TAG, "Allow after created: " + mAllowList);
166                 mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
167             }
168             return mService;
169         }
170 
waitOnDisconnected()171         public void waitOnDisconnected() throws InterruptedException {
172             await(mDisconnected, "not disconnected");
173         }
174 
setAllowSelf()175         public void setAllowSelf() {
176             final ArraySet<String> pkgs = new ArraySet<>(1);
177             pkgs.add(SERVICE_PACKAGE);
178             mAllowList = new Pair<>(pkgs, null);
179         }
180 
await(@onNull CountDownLatch latch, @NonNull String fmt, @Nullable Object... args)181         private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
182                 @Nullable Object... args)
183                 throws InterruptedException {
184             final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
185             if (!called) {
186                 throw new IllegalStateException(String.format(fmt, args)
187                         + " in " + GENERIC_TIMEOUT_MS + "ms");
188             }
189         }
190     }
191 }
192