blob: bd91333100bd46044f44970532e237091254a290 [file] [log] [blame]
Dianne Hackborn4c62fc02009-08-08 20:40:27 -07001/*
2 * Copyright (C) 2009 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
Dianne Hackbornba398392011-08-01 16:11:57 -070017package com.android.systemui;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070018
Ahan Wufa42c512019-05-15 19:52:51 +080019import android.app.ActivityManager;
wilsonshih3b2683a2019-01-19 17:01:19 +080020import android.content.Context;
Dianne Hackborn759a39e2009-08-09 17:20:27 -070021import android.graphics.Rect;
Ahan Wu44ab58a2019-06-20 18:14:29 +080022import android.os.HandlerThread;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070023import android.service.wallpaper.WallpaperService;
Ahan Wufa42c512019-05-15 19:52:51 +080024import android.util.Log;
25import android.util.Size;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070026import android.view.SurfaceHolder;
Romain Guy407ec782011-08-24 17:06:58 -070027
Ahan Wufa42c512019-05-15 19:52:51 +080028import com.android.internal.annotations.VisibleForTesting;
29import com.android.systemui.glwallpaper.EglHelper;
30import com.android.systemui.glwallpaper.GLWallpaperRenderer;
Ahan Wu67e7f102019-01-14 20:38:14 +080031import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
Ahan Wufa42c512019-05-15 19:52:51 +080032import com.android.systemui.plugins.statusbar.StatusBarStateController;
33import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
34import com.android.systemui.statusbar.StatusBarState;
35import com.android.systemui.statusbar.phone.DozeParameters;
Valentin Iftime6239e532018-08-24 17:17:26 +020036
Ahan Wu76884242019-05-22 20:04:23 +080037import java.io.FileDescriptor;
38import java.io.PrintWriter;
39
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070040/**
41 * Default built-in wallpaper that simply shows a static image.
42 */
Romain Guy407ec782011-08-24 17:06:58 -070043@SuppressWarnings({"UnusedDeclaration"})
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070044public class ImageWallpaper extends WallpaperService {
Ahan Wub4924522019-02-20 19:15:04 +080045 private static final String TAG = ImageWallpaper.class.getSimpleName();
Ahan Wufa42c512019-05-15 19:52:51 +080046 // We delayed destroy render context that subsequent render requests have chance to cancel it.
47 // This is to avoid destroying then recreating render context in a very short time.
48 private static final int DELAY_FINISH_RENDERING = 1000;
Ahan Wub3780592019-07-04 20:50:00 +080049 private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
Ahan Wu23ac8e62019-07-23 20:41:52 +080050 private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
Ahan Wu44ab58a2019-06-20 18:14:29 +080051 private HandlerThread mWorker;
52
53 @Override
54 public void onCreate() {
55 super.onCreate();
56 mWorker = new HandlerThread(TAG);
57 mWorker.start();
58 }
Dianne Hackborn759a39e2009-08-09 17:20:27 -070059
Romain Guyef654bd2009-08-11 19:12:17 -070060 @Override
Romain Guyef654bd2009-08-11 19:12:17 -070061 public Engine onCreateEngine() {
Ahan Wu67e7f102019-01-14 20:38:14 +080062 return new GLEngine(this);
Romain Guyef654bd2009-08-11 19:12:17 -070063 }
64
Ahan Wu44ab58a2019-06-20 18:14:29 +080065 @Override
66 public void onDestroy() {
67 super.onDestroy();
68 mWorker.quitSafely();
69 mWorker = null;
70 }
71
Ahan Wufa42c512019-05-15 19:52:51 +080072 class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
73 // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
74 // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
75 @VisibleForTesting
76 static final int MIN_SURFACE_WIDTH = 64;
77 @VisibleForTesting
78 static final int MIN_SURFACE_HEIGHT = 64;
79
80 private GLWallpaperRenderer mRenderer;
81 private EglHelper mEglHelper;
82 private StatusBarStateController mController;
83 private final Runnable mFinishRenderingTask = this::finishRendering;
84 private final boolean mNeedTransition;
Ahan Wub3780592019-07-04 20:50:00 +080085 private final Object mMonitor = new Object();
Ahan Wufa42c512019-05-15 19:52:51 +080086 private boolean mNeedRedraw;
Ahan Wub3780592019-07-04 20:50:00 +080087 // This variable can only be accessed in synchronized block.
88 private boolean mWaitingForRendering;
Ahan Wu67e7f102019-01-14 20:38:14 +080089
90 GLEngine(Context context) {
Ahan Wufa42c512019-05-15 19:52:51 +080091 mNeedTransition = ActivityManager.isHighEndGfx()
92 && !DozeParameters.getInstance(context).getDisplayNeedsBlanking();
93
94 // We will preserve EGL context when we are in lock screen or aod
95 // to avoid janking in following transition, we need to release when back to home.
96 mController = Dependency.get(StatusBarStateController.class);
97 if (mController != null) {
98 mController.addCallback(this /* StateListener */);
99 }
100 mEglHelper = new EglHelper();
101 mRenderer = new ImageWallpaperRenderer(context, this /* SurfaceProxy */);
102 }
103
104 @Override
105 public void onCreate(SurfaceHolder surfaceHolder) {
106 setFixedSizeAllowed(true);
Ahan Wue16e1fa2019-05-29 18:39:33 +0800107 setOffsetNotificationsEnabled(true);
Ahan Wufa42c512019-05-15 19:52:51 +0800108 updateSurfaceSize();
109 }
110
111 private void updateSurfaceSize() {
112 SurfaceHolder holder = getSurfaceHolder();
113 Size frameSize = mRenderer.reportSurfaceSize();
114 int width = Math.max(MIN_SURFACE_WIDTH, frameSize.getWidth());
115 int height = Math.max(MIN_SURFACE_HEIGHT, frameSize.getHeight());
116 holder.setFixedSize(width, height);
Ahan Wu67e7f102019-01-14 20:38:14 +0800117 }
118
119 @Override
Ahan Wue16e1fa2019-05-29 18:39:33 +0800120 public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
121 float yOffsetStep, int xPixelOffset, int yPixelOffset) {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800122 mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
Ahan Wue16e1fa2019-05-29 18:39:33 +0800123 }
124
125 @Override
Ahan Wu67e7f102019-01-14 20:38:14 +0800126 public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
Ahan Wu23ac8e62019-07-23 20:41:52 +0800127 if (!mNeedTransition) return;
Ahan Wu44ab58a2019-06-20 18:14:29 +0800128 mWorker.getThreadHandler().post(
Ahan Wu23ac8e62019-07-23 20:41:52 +0800129 () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
130 if (inAmbientMode && animationDuration == 0) {
Ahan Wub3780592019-07-04 20:50:00 +0800131 // This means that we are transiting from home to aod, to avoid
132 // race condition between window visibility and transition,
133 // we don't return until the transition is finished. See b/136643341.
134 waitForBackgroundRendering();
135 }
136 }
137
138 private void waitForBackgroundRendering() {
139 synchronized (mMonitor) {
140 try {
141 mWaitingForRendering = true;
142 for (int patience = 1; mWaitingForRendering; patience++) {
143 mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
144 mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
145 }
146 } catch (InterruptedException ex) {
147 } finally {
148 mWaitingForRendering = false;
149 }
150 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800151 }
152
Ahan Wu48ebbd72019-03-12 20:59:13 +0800153 @Override
154 public void onDestroy() {
Ahan Wufa42c512019-05-15 19:52:51 +0800155 if (mController != null) {
156 mController.removeCallback(this /* StateListener */);
Ahan Wu48ebbd72019-03-12 20:59:13 +0800157 }
Ahan Wufa42c512019-05-15 19:52:51 +0800158 mController = null;
Ahan Wu44ab58a2019-06-20 18:14:29 +0800159
160 mWorker.getThreadHandler().post(() -> {
161 mRenderer.finish();
162 mRenderer = null;
163 mEglHelper.finish();
164 mEglHelper = null;
165 getSurfaceHolder().getSurface().hwuiDestroy();
166 });
Ahan Wu48ebbd72019-03-12 20:59:13 +0800167 }
168
Ahan Wufa42c512019-05-15 19:52:51 +0800169 @Override
170 public void onSurfaceCreated(SurfaceHolder holder) {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800171 mWorker.getThreadHandler().post(() -> {
172 mEglHelper.init(holder);
173 mRenderer.onSurfaceCreated();
174 });
Ahan Wufa42c512019-05-15 19:52:51 +0800175 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800176
Ahan Wufa42c512019-05-15 19:52:51 +0800177 @Override
178 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800179 mWorker.getThreadHandler().post(() -> {
180 mRenderer.onSurfaceChanged(width, height);
181 mNeedRedraw = true;
182 });
Ahan Wufa42c512019-05-15 19:52:51 +0800183 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800184
Ahan Wufa42c512019-05-15 19:52:51 +0800185 @Override
186 public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800187 mWorker.getThreadHandler().post(() -> {
188 if (mNeedRedraw) {
189 preRender();
190 requestRender();
191 postRender();
192 mNeedRedraw = false;
193 }
194 });
Ahan Wufa42c512019-05-15 19:52:51 +0800195 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800196
Ahan Wufa42c512019-05-15 19:52:51 +0800197 @Override
198 public void onStatePostChange() {
199 // When back to home, we try to release EGL, which is preserved in lock screen or aod.
200 if (mController.getState() == StatusBarState.SHADE) {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800201 mWorker.getThreadHandler().post(this::scheduleFinishRendering);
Ahan Wufa42c512019-05-15 19:52:51 +0800202 }
203 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800204
Ahan Wufa42c512019-05-15 19:52:51 +0800205 @Override
206 public void preRender() {
Ahan Wub3780592019-07-04 20:50:00 +0800207 // This method should only be invoked from worker thread.
208 preRenderInternal();
Ahan Wu44ab58a2019-06-20 18:14:29 +0800209 }
210
211 private void preRenderInternal() {
Ahan Wufa42c512019-05-15 19:52:51 +0800212 boolean contextRecreated = false;
213 Rect frame = getSurfaceHolder().getSurfaceFrame();
Ahan Wu44ab58a2019-06-20 18:14:29 +0800214 cancelFinishRenderingTask();
Ahan Wu67e7f102019-01-14 20:38:14 +0800215
Ahan Wufa42c512019-05-15 19:52:51 +0800216 // Check if we need to recreate egl context.
217 if (!mEglHelper.hasEglContext()) {
218 mEglHelper.destroyEglSurface();
219 if (!mEglHelper.createEglContext()) {
220 Log.w(TAG, "recreate egl context failed!");
221 } else {
222 contextRecreated = true;
223 }
224 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800225
Ahan Wufa42c512019-05-15 19:52:51 +0800226 // Check if we need to recreate egl surface.
227 if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
228 if (!mEglHelper.createEglSurface(getSurfaceHolder())) {
229 Log.w(TAG, "recreate egl surface failed!");
230 }
231 }
232
233 // If we recreate egl context, notify renderer to setup again.
234 if (mEglHelper.hasEglContext() && mEglHelper.hasEglSurface() && contextRecreated) {
235 mRenderer.onSurfaceCreated();
236 mRenderer.onSurfaceChanged(frame.width(), frame.height());
237 }
238 }
239
240 @Override
241 public void requestRender() {
Ahan Wub3780592019-07-04 20:50:00 +0800242 // This method should only be invoked from worker thread.
243 requestRenderInternal();
Ahan Wu44ab58a2019-06-20 18:14:29 +0800244 }
245
246 private void requestRenderInternal() {
Ahan Wufa42c512019-05-15 19:52:51 +0800247 Rect frame = getSurfaceHolder().getSurfaceFrame();
248 boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
249 && frame.width() > 0 && frame.height() > 0;
250
251 if (readyToRender) {
252 mRenderer.onDrawFrame();
253 if (!mEglHelper.swapBuffer()) {
254 Log.e(TAG, "drawFrame failed!");
255 }
256 } else {
257 Log.e(TAG, "requestRender: not ready, has context=" + mEglHelper.hasEglContext()
258 + ", has surface=" + mEglHelper.hasEglSurface()
259 + ", frame=" + frame);
260 }
261 }
262
263 @Override
264 public void postRender() {
Ahan Wub3780592019-07-04 20:50:00 +0800265 // This method should only be invoked from worker thread.
266 notifyWaitingThread();
267 scheduleFinishRendering();
268 }
269
270 private void notifyWaitingThread() {
271 synchronized (mMonitor) {
272 if (mWaitingForRendering) {
273 try {
274 mWaitingForRendering = false;
275 mMonitor.notify();
276 } catch (IllegalMonitorStateException ex) {
277 }
278 }
279 }
Ahan Wu44ab58a2019-06-20 18:14:29 +0800280 }
281
282 private void cancelFinishRenderingTask() {
283 mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
Ahan Wufa42c512019-05-15 19:52:51 +0800284 }
285
286 private void scheduleFinishRendering() {
Ahan Wu44ab58a2019-06-20 18:14:29 +0800287 cancelFinishRenderingTask();
288 mWorker.getThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
Ahan Wufa42c512019-05-15 19:52:51 +0800289 }
290
291 private void finishRendering() {
292 if (mEglHelper != null) {
293 mEglHelper.destroyEglSurface();
294 if (!needPreserveEglContext()) {
295 mEglHelper.destroyEglContext();
296 }
297 }
298 }
299
300 private boolean needPreserveEglContext() {
301 return mNeedTransition && mController != null
302 && mController.getState() == StatusBarState.KEYGUARD;
303 }
Ahan Wu76884242019-05-22 20:04:23 +0800304
305 @Override
306 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
307 super.dump(prefix, fd, out, args);
308 out.print(prefix); out.print("Engine="); out.println(this);
309
310 boolean isHighEndGfx = ActivityManager.isHighEndGfx();
311 out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx);
312
313 DozeParameters dozeParameters = DozeParameters.getInstance(getApplicationContext());
314 out.print(prefix); out.print("displayNeedsBlanking=");
315 out.println(dozeParameters != null ? dozeParameters.getDisplayNeedsBlanking() : "null");
316
317 out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
318 out.print(prefix); out.print("StatusBarState=");
319 out.println(mController != null ? mController.getState() : "null");
320
321 out.print(prefix); out.print("valid surface=");
322 out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
323 ? getSurfaceHolder().getSurface().isValid()
324 : "null");
325
326 out.print(prefix); out.print("surface frame=");
327 out.println(getSurfaceHolder() != null ? getSurfaceHolder().getSurfaceFrame() : "null");
328
329 mEglHelper.dump(prefix, fd, out, args);
330 mRenderer.dump(prefix, fd, out, args);
331 }
Ahan Wu67e7f102019-01-14 20:38:14 +0800332 }
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700333}