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.app.stubs.shared;
18 
19 import android.accessibilityservice.AccessibilityService;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.PixelFormat;
23 import android.os.ConditionVariable;
24 import android.view.View;
25 import android.view.WindowManager;
26 import android.view.WindowManager.LayoutParams;
27 import android.view.accessibility.AccessibilityEvent;
28 
29 import java.util.concurrent.CompletableFuture;
30 import java.util.concurrent.Future;
31 
32 /** Accessibility service that posts a window as soon as it's enabled. */
33 public class AppAccessibilityService extends AccessibilityService {
34     private static final int BACKGROUND_COLOR = 0xFFFF0000;
35     private static volatile CompletableFuture<AppAccessibilityService> sServiceFuture =
36             new CompletableFuture<>();
37 
getConnected()38     public static Future<AppAccessibilityService> getConnected() {
39         return sServiceFuture;
40     }
41 
42     private WindowManager mWindowManager;
43     private View mView;
44 
45     /**
46      * This doesn't need to be volatile because of the inner sync barriers of sServiceFuture. It's
47      * set before sServiceFuture.obtrudeValue() and read after sServiceFuture.get().
48      */
49     private ConditionVariable mWindowAdded;
50 
51     @Override
onCreate()52     public void onCreate() {
53         super.onCreate();
54         mWindowManager = getSystemService(WindowManager.class);
55     }
56 
57     /** Always call after {@link #getConnected()}}. */
waitWindowAdded(long timeoutMs)58     public boolean waitWindowAdded(long timeoutMs) {
59         return mWindowAdded.block(timeoutMs);
60     }
61 
62     @Override
onAccessibilityEvent(AccessibilityEvent event)63     public void onAccessibilityEvent(AccessibilityEvent event) {}
64 
65     @Override
onInterrupt()66     public void onInterrupt() {}
67 
68     @Override
onServiceConnected()69     protected void onServiceConnected() {
70         mWindowAdded = new ConditionVariable();
71         sServiceFuture.obtrudeValue(this);
72         mView = new CustomView(this);
73         mView.setBackgroundColor(BACKGROUND_COLOR);
74         LayoutParams params =
75                 new LayoutParams(
76                         200,
77                         200,
78                         LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
79                         LayoutParams.FLAG_NOT_TOUCH_MODAL,
80                         PixelFormat.TRANSLUCENT);
81         mWindowManager.addView(mView, params);
82     }
83 
84     @Override
onDestroy()85     public void onDestroy() {
86         sServiceFuture = new CompletableFuture<>();
87         if (mView != null) {
88             mWindowManager.removeViewImmediate(mView);
89         }
90     }
91 
92     private class CustomView extends View {
CustomView(Context context)93         CustomView(Context context) {
94             super(context);
95         }
96         @Override
onDraw(Canvas canvas)97         protected void onDraw(Canvas canvas) {
98             super.onDraw(canvas);
99             mWindowAdded.open();
100         }
101     }
102 }
103 
104