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