• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.app.cts.wallpapers
17 
18 import android.app.WallpaperColors
19 import android.os.Bundle
20 import android.os.Looper
21 import android.service.wallpaper.WallpaperService
22 import android.util.Log
23 import android.view.MotionEvent
24 import android.view.SurfaceHolder
25 import android.view.WindowInsets
26 import com.google.common.truth.Truth.assertWithMessage
27 
28 /**
29  * Wrapper for WallpaperService.
30  * This class does not add any logic or change any function.
31  * It verifies that the public callback methods from [WallpaperService.Engine]
32  * are called by the main thread.
33  * The callback methods are the methods overridden by this class.
34  *
35  * It also includes a few checks to verify that the methods are called in a proper order.
36  * For example, many methods should only be called after [WallpaperService.Engine.onSurfaceCreated],
37  * which itself should only be called after [WallpaperService.Engine.onCreate].
38  */
39 abstract class TestWallpaperService : WallpaperService() {
40 
41     private val mainThread: Thread = Looper.getMainLooper().thread
42     companion object {
43         private val TAG = TestWallpaperService::class.java.simpleName
44         private const val DEBUG = true
45         private var assertionError: AssertionError? = null
46 
47         /**
48          * Tracks the number of times [FakeEngine.onCreate] is called
49          */
50         var createCount: Int = 0
51 
52         /**
53          * Tracks the number of times [FakeEngine.onDestroy] is called
54          */
55         var destroyCount: Int = 0
56 
resetCountsnull57         fun resetCounts() {
58             createCount = 0
59             destroyCount = 0
60         }
61 
62         /**
63          * To be called at the end of tests requiring assertion checks from this class.
64          * The first assertion error encountered by this class, if there is any,
65          * will be raised when calling this function.
66          * We use this to avoid raising errors directly in the callback methods, since errors in
67          * callback methods could be raised from the main thread and crash the entire test process.
68          */
checkAssertionsnull69         fun checkAssertions() {
70             assertionError?.let { throw assertionError!! }
71             assertionError = null
72         }
73     }
74 
onCreateEnginenull75     override fun onCreateEngine(): Engine {
76         if (DEBUG) Log.d(TAG, "onCreateEngine")
77         assertMainThread()
78         return FakeEngine()
79     }
80 
81     internal inner class FakeEngine : Engine() {
82         private var mCreated = false
83         private var mSurfaceCreated = false
84 
drawnull85         private fun draw(holder: SurfaceHolder) {
86             val c = holder.lockCanvas()
87             c.drawColor(getColor())
88             holder.unlockCanvasAndPost(c)
89         }
90 
onAmbientModeChangednull91         override fun onAmbientModeChanged(inAmbientMode: Boolean, animationDuration: Long) {
92             if (DEBUG) Log.d(TAG, "onAmbientModeChanged")
93             assertMainThread()
94             super.onAmbientModeChanged(inAmbientMode, animationDuration)
95         }
96 
onApplyWindowInsetsnull97         override fun onApplyWindowInsets(insets: WindowInsets) {
98             if (DEBUG) Log.d(TAG, "onApplyWindowInsets")
99             assertMainThread()
100             super.onApplyWindowInsets(insets)
101         }
102 
onCommandnull103         override fun onCommand(
104             action: String,
105             x: Int,
106             y: Int,
107             z: Int,
108             extras: Bundle?,
109             resultRequested: Boolean
110         ): Bundle? {
111             if (DEBUG) Log.d(TAG, "onCommand")
112             assertMainThread()
113             return super.onCommand(action, x, y, z, extras, resultRequested)
114         }
115 
onComputeColorsnull116         override fun onComputeColors(): WallpaperColors? {
117             if (DEBUG) Log.d(TAG, "onComputeColors")
118             assertMainThread()
119             return super.onComputeColors()
120         }
121 
onCreatenull122         override fun onCreate(surfaceHolder: SurfaceHolder) {
123             if (DEBUG) Log.d(TAG, "onCreate")
124             assertMainThread()
125             assertNotCreated()
126             assertSurfaceNotCreated()
127             mCreated = true
128             createCount++
129             super.onCreate(surfaceHolder)
130         }
131 
onDesiredSizeChangednull132         override fun onDesiredSizeChanged(desiredWidth: Int, desiredHeight: Int) {
133             if (DEBUG) Log.d(TAG, "onDesiredSizeChanged")
134             assertMainThread()
135             assertCreated()
136             assertSurfaceCreated()
137             super.onDesiredSizeChanged(desiredWidth, desiredHeight)
138         }
139 
onDestroynull140         override fun onDestroy() {
141             if (DEBUG) Log.d(TAG, "onDestroy, new count=" + (destroyCount + 1))
142             assertMainThread()
143             assertCreated()
144             mCreated = false
145             destroyCount++
146             super.onDestroy()
147         }
148 
onOffsetsChangednull149         override fun onOffsetsChanged(
150             xOffset: Float,
151             yOffset: Float,
152             xOffsetStep: Float,
153             yOffsetStep: Float,
154             xPixelOffset: Int,
155             yPixelOffset: Int
156         ) {
157             if (DEBUG) Log.d(TAG, "onOffsetsChanged")
158             assertMainThread()
159             super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep,
160                 xPixelOffset, yPixelOffset)
161         }
162 
onSurfaceChangednull163         override fun onSurfaceChanged(
164             holder: SurfaceHolder,
165             format: Int,
166             width: Int,
167             height: Int
168         ) {
169             if (DEBUG) Log.d(TAG, "onSurfaceChanged")
170             assertMainThread()
171             assertCreated()
172             assertSurfaceCreated()
173             super.onSurfaceChanged(holder, format, width, height)
174         }
175 
onSurfaceCreatednull176         override fun onSurfaceCreated(holder: SurfaceHolder) {
177             if (DEBUG) Log.d(TAG, "onSurfaceCreated")
178             assertMainThread()
179             assertCreated()
180             assertSurfaceNotCreated()
181             mSurfaceCreated = true
182             super.onSurfaceCreated(holder)
183         }
184 
onSurfaceDestroyednull185         override fun onSurfaceDestroyed(holder: SurfaceHolder) {
186             if (DEBUG) Log.d(TAG, "onSurfaceDestroyed")
187             assertMainThread()
188             assertCreated()
189             assertSurfaceCreated()
190             super.onSurfaceDestroyed(holder)
191         }
192 
onSurfaceRedrawNeedednull193         override fun onSurfaceRedrawNeeded(holder: SurfaceHolder) {
194             if (DEBUG) Log.d(TAG, "onSurfaceRedrawNeeded")
195             draw(holder)
196             assertMainThread()
197             assertCreated()
198             assertSurfaceCreated()
199             super.onSurfaceRedrawNeeded(holder)
200         }
201 
onTouchEventnull202         override fun onTouchEvent(event: MotionEvent) {
203             if (DEBUG) Log.d(TAG, "onTouchEvent")
204             assertMainThread()
205             super.onTouchEvent(event)
206         }
207 
onVisibilityChangednull208         override fun onVisibilityChanged(visible: Boolean) {
209             if (DEBUG) Log.d(TAG, "onVisibilityChanged")
210             assertMainThread()
211             super.onVisibilityChanged(visible)
212         }
213 
onZoomChangednull214         override fun onZoomChanged(zoom: Float) {
215             if (DEBUG) Log.d(TAG, "onZoomChanged")
216             assertMainThread()
217             super.onZoomChanged(zoom)
218         }
219 
assertCreatednull220         private fun assertCreated() {
221             assertHelper {
222                 assertWithMessage(
223                     "Engine must be created (with onCreate) " +
224                     "and not destroyed before calling this function")
225                     .that(mCreated).isTrue()
226             }
227         }
228 
assertSurfaceCreatednull229         private fun assertSurfaceCreated() {
230             assertHelper {
231                 assertWithMessage(
232                     "Surface must be created (with onSurfaceCreated) " +
233                     "and not destroyed before calling this function")
234                     .that(mSurfaceCreated).isTrue()
235             }
236         }
237 
assertNotCreatednull238         private fun assertNotCreated() {
239             assertHelper {
240                 assertWithMessage(
241                     "Engine must not be created (with onCreate) " +
242                     "or must be destroyed before calling this function")
243                     .that(mCreated).isFalse()
244             }
245         }
246 
assertSurfaceNotCreatednull247         private fun assertSurfaceNotCreated() {
248             assertHelper {
249                 assertWithMessage(
250                     "Surface must not be created (with onSurfaceCreated) " +
251                     "or must be destroyed before calling this function")
252                     .that(mSurfaceCreated).isFalse()
253             }
254         }
255     }
256 
257     /**
258      * Check that the current thread is the main thread
259      */
assertMainThreadnull260     private fun assertMainThread() {
261         assertHelper {
262             val callerThread = Thread.currentThread()
263             assertWithMessage(
264                 "Callback methods from WallpaperService.Engine " +
265                     "must be called by the main thread; but was called by " + callerThread)
266                 .that(callerThread).isSameInstanceAs(mainThread)
267         }
268     }
269 
270     /**
271      * Run an executable that performs some assertions. If any assertion is raised, and it is the
272      * first one raised so far, store it.
273      */
assertHelpernull274     private fun assertHelper(check: Runnable) {
275         try {
276             check.run()
277         } catch (error: AssertionError) {
278             assertionError = assertionError ?: error
279         }
280     }
281 
282     /**
283      * The color that this test wallpaper should draw, for debug purposes.
284      */
getColornull285     protected abstract fun getColor(): Int
286 }
287