• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.wallpaper.module;
17 
18 import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
19 import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
20 
21 import android.annotation.SuppressLint;
22 import android.app.WallpaperColors;
23 import android.app.WallpaperManager;
24 import android.content.BroadcastReceiver;
25 import android.content.ComponentCallbacks2;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.graphics.Bitmap;
30 import android.graphics.BitmapFactory;
31 import android.graphics.Canvas;
32 import android.graphics.Point;
33 import android.graphics.Rect;
34 import android.graphics.RectF;
35 import android.graphics.drawable.BitmapDrawable;
36 import android.graphics.drawable.Drawable;
37 import android.opengl.GLES20;
38 import android.opengl.GLUtils;
39 import android.os.AsyncTask;
40 import android.os.Build.VERSION;
41 import android.os.Build.VERSION_CODES;
42 import android.os.Handler;
43 import android.renderscript.Matrix4f;
44 import android.service.wallpaper.WallpaperService;
45 import android.util.Log;
46 import android.view.Display;
47 import android.view.MotionEvent;
48 import android.view.SurfaceHolder;
49 import android.view.WindowManager;
50 
51 import com.android.wallpaper.util.ScreenSizeCalculator;
52 
53 import java.io.FileDescriptor;
54 import java.io.FileInputStream;
55 import java.io.FileNotFoundException;
56 import java.io.IOException;
57 import java.io.PrintWriter;
58 import java.nio.ByteBuffer;
59 import java.nio.ByteOrder;
60 import java.nio.FloatBuffer;
61 
62 import javax.microedition.khronos.egl.EGL10;
63 import javax.microedition.khronos.egl.EGLConfig;
64 import javax.microedition.khronos.egl.EGLContext;
65 import javax.microedition.khronos.egl.EGLDisplay;
66 import javax.microedition.khronos.egl.EGLSurface;
67 
68 import androidx.annotation.RequiresApi;
69 
70 /**
71  * Live wallpaper service which simply renders a wallpaper from internal storage. Designed as a
72  * workaround to WallpaperManager not having an allowBackup=false option on pre-N builds of Android.
73  * <p>
74  * Adapted from {@code com.android.systemui.ImageWallpaper}.
75  */
76 @SuppressLint("ServiceCast")
77 public class NoBackupImageWallpaper extends WallpaperService {
78 
79     public static final String ACTION_ROTATING_WALLPAPER_CHANGED =
80             ".ACTION_ROTATING_WALLPAPER_CHANGED";
81     public static final String PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED =
82             ".NOTIFY_ROTATING_WALLPAPER_CHANGED";
83     public static final String PREVIEW_WALLPAPER_FILE_PATH = "preview_wallpaper.jpg";
84     public static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg";
85 
86     private static final String TAG = "NoBackupImageWallpaper";
87     private static final String GL_LOG_TAG = "ImageWallpaperGL";
88     private static final boolean DEBUG = false;
89     private static final boolean FIXED_SIZED_SURFACE = false;
90 
91     private final Handler mHandler = new Handler();
92 
93     private int mOpenGlContextCounter;
94     private WallpaperManager mWallpaperManager;
95     private DrawableEngine mEngine;
96     private boolean mIsHardwareAccelerated;
97 
98     @Override
onCreate()99     public void onCreate() {
100         super.onCreate();
101 
102         mOpenGlContextCounter = 0;
103         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
104 
105         // By default, use OpenGL for drawing the static wallpaper image.
106         mIsHardwareAccelerated = true;
107     }
108 
109     @Override
onTrimMemory(int level)110     public void onTrimMemory(int level) {
111         if (mEngine != null) {
112             mEngine.trimMemory(level);
113         }
114     }
115 
116     @Override
onCreateEngine()117     public Engine onCreateEngine() {
118         mEngine = new DrawableEngine();
119         return mEngine;
120     }
121 
122     private class DrawableEngine extends Engine {
123         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
124         static final int EGL_OPENGL_ES2_BIT = 4;
125         private static final String S_SIMPLE_VS =
126                 "attribute vec4 position;\n"
127                         + "attribute vec2 texCoords;\n"
128                         + "varying vec2 outTexCoords;\n"
129                         + "uniform mat4 projection;\n"
130                         + "\nvoid main(void) {\n"
131                         + "    outTexCoords = texCoords;\n"
132                         + "    gl_Position = projection * position;\n"
133                         + "}\n\n";
134         private static final String S_SIMPLE_FS =
135                 "precision mediump float;\n\n"
136                         + "varying vec2 outTexCoords;\n"
137                         + "uniform sampler2D texture;\n"
138                         + "\nvoid main(void) {\n"
139                         + "    gl_FragColor = texture2D(texture, outTexCoords);\n"
140                         + "}\n\n";
141         private static final int FLOAT_SIZE_BYTES = 4;
142         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
143         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
144         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
145         Bitmap mBackground;
146         WallpaperColors mCachedWallpaperColors;
147         int mBackgroundWidth = -1, mBackgroundHeight = -1;
148         int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
149         int mLastRotation = -1;
150         float mXOffset = 0.5f;
151         float mYOffset = 0.5f;
152         float mScale = 1f;
153         boolean mVisible = true;
154         boolean mOffsetsChanged;
155         int mLastXTranslation;
156         int mLastYTranslation;
157         private Display mDefaultDisplay;
158         private EGL10 mEgl;
159         private EGLDisplay mEglDisplay;
160         private EGLConfig mEglConfig;
161         private EGLContext mEglContext;
162         private EGLSurface mEglSurface;
163         private int mTexture;
164         private int mProgram;
165         private boolean mIsOpenGlTextureLoaded;
166         private int mRotationAtLastSurfaceSizeUpdate = -1;
167         private int mDisplayWidthAtLastSurfaceSizeUpdate = -1;
168         private int mDisplayHeightAtLastSurfaceSizeUpdate = -1;
169 
170         private int mLastRequestedWidth = -1;
171         private int mLastRequestedHeight = -1;
172         private AsyncTask<Void, Void, Bitmap> mLoader;
173         private boolean mNeedsDrawAfterLoadingWallpaper;
174         private boolean mSurfaceValid;
175 
176         private BroadcastReceiver mReceiver;
177 
DrawableEngine()178         public DrawableEngine() {
179             super();
180         }
181 
trimMemory(int level)182         public void trimMemory(int level) {
183             if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
184                     && mBackground != null) {
185                 if (DEBUG) {
186                     Log.d(TAG, "trimMemory");
187                 }
188                 mBackground.recycle();
189                 mBackground = null;
190                 mBackgroundWidth = -1;
191                 mBackgroundHeight = -1;
192             }
193         }
194 
195         @Override
onCreate(SurfaceHolder surfaceHolder)196         public void onCreate(SurfaceHolder surfaceHolder) {
197             if (DEBUG) {
198                 Log.d(TAG, "onCreate");
199             }
200 
201             super.onCreate(surfaceHolder);
202 
203             mIsOpenGlTextureLoaded = false;
204 
205             mDefaultDisplay = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
206                     .getDefaultDisplay();
207 
208             updateSurfaceSize(surfaceHolder, mDefaultDisplay, false /* forDraw */);
209 
210             // Enable offset notifications to pan wallpaper for parallax effect.
211             setOffsetNotificationsEnabled(true);
212 
213             // If not a preview, then register a local broadcast receiver for listening to changes in the
214             // rotating wallpaper file.
215             if (!isPreview()) {
216                 IntentFilter filter = new IntentFilter();
217                 filter.addAction(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED);
218 
219                 mReceiver = new BroadcastReceiver() {
220                     @Override
221                     public void onReceive(Context context, Intent intent) {
222                         if (DEBUG) {
223                             Log.i(TAG, "Broadcast received with intent: " + intent);
224                         }
225 
226                         String action = intent.getAction();
227                         if (action.equals(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED)) {
228                             DrawableEngine.this.invalidateAndRedrawWallpaper();
229                         }
230                     }
231                 };
232 
233                 registerReceiver(mReceiver, filter, getPackageName()
234                         + PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED, null /* handler */);
235             }
236         }
237 
238         @Override
onDestroy()239         public void onDestroy() {
240             super.onDestroy();
241             mBackground = null;
242             mWallpaperManager.forgetLoadedWallpaper();
243 
244             if (!isPreview() && mReceiver != null) {
245                 unregisterReceiver(mReceiver);
246             }
247         }
248 
updateSurfaceSize(SurfaceHolder surfaceHolder, Display display, boolean forDraw)249         boolean updateSurfaceSize(SurfaceHolder surfaceHolder, Display display, boolean forDraw) {
250             boolean hasWallpaper = true;
251             Point displaySize = ScreenSizeCalculator.getInstance().getScreenSize(display);
252 
253             // Load background image dimensions, if we haven't saved them yet
254             if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
255                 // Need to load the image to get dimensions
256                 loadWallpaper(forDraw);
257                 if (DEBUG) {
258                     Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
259                 }
260                 hasWallpaper = false;
261             }
262 
263             // Force the wallpaper to cover the screen in both dimensions
264             int surfaceWidth = Math.max(displaySize.x, mBackgroundWidth);
265             int surfaceHeight = Math.max(displaySize.y, mBackgroundHeight);
266 
267             if (FIXED_SIZED_SURFACE) {
268                 // Used a fixed size surface, because we are special.  We can do
269                 // this because we know the current design of window animations doesn't
270                 // cause this to break.
271                 surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
272                 mLastRequestedWidth = surfaceWidth;
273                 mLastRequestedHeight = surfaceHeight;
274             } else {
275                 surfaceHolder.setSizeFromLayout();
276             }
277             return hasWallpaper;
278         }
279 
280         @Override
onVisibilityChanged(boolean visible)281         public void onVisibilityChanged(boolean visible) {
282             if (DEBUG) {
283                 Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
284             }
285 
286             if (mVisible != visible) {
287                 if (DEBUG) {
288                     Log.d(TAG, "Visibility changed to visible=" + visible);
289                 }
290                 mVisible = visible;
291                 drawFrame(false /* forceRedraw */);
292             }
293         }
294 
295         @Override
onTouchEvent(MotionEvent event)296         public void onTouchEvent(MotionEvent event) {
297             super.onTouchEvent(event);
298         }
299 
300         @Override
onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixels, int yPixels)301         public void onOffsetsChanged(float xOffset, float yOffset,
302                                      float xOffsetStep, float yOffsetStep,
303                                      int xPixels, int yPixels) {
304             if (DEBUG) {
305                 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
306                         + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
307                         + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
308             }
309 
310             if (mXOffset != xOffset || mYOffset != yOffset) {
311                 if (DEBUG) {
312                     Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
313                 }
314                 mXOffset = xOffset;
315                 mYOffset = yOffset;
316                 mOffsetsChanged = true;
317             }
318             mHandler.post(new Runnable() {
319                 @Override
320                 public void run() {
321                     drawFrame(false /* forceRedraw */);
322                 }
323             });
324         }
325 
326         @Override
onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)327         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
328             if (DEBUG) {
329                 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
330             }
331 
332             super.onSurfaceChanged(holder, format, width, height);
333 
334             // Retrieve buffer in new size.
335             if (mEgl != null) {
336                 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
337             }
338             drawFrame(false /* forceRedraw */);
339         }
340 
341         @Override
onSurfaceDestroyed(SurfaceHolder holder)342         public void onSurfaceDestroyed(SurfaceHolder holder) {
343             super.onSurfaceDestroyed(holder);
344             if (DEBUG) {
345                 Log.d(TAG, "onSurfaceDestroyed");
346             }
347             mLastSurfaceWidth = mLastSurfaceHeight = -1;
348             mSurfaceValid = false;
349 
350             if (mIsHardwareAccelerated) {
351                 finishGL(mTexture, mProgram);
352             }
353         }
354 
355         @Override
onSurfaceCreated(SurfaceHolder holder)356         public void onSurfaceCreated(SurfaceHolder holder) {
357             super.onSurfaceCreated(holder);
358             if (DEBUG) {
359                 Log.d(TAG, "onSurfaceCreated");
360             }
361             mLastSurfaceWidth = mLastSurfaceHeight = -1;
362             mSurfaceValid = true;
363 
364             if (mIsHardwareAccelerated) {
365                 if (!initGL(holder)) {
366                     // Fall back to canvas drawing if initializing OpenGL failed.
367                     mIsHardwareAccelerated = false;
368                     mEgl = null;
369                 }
370             }
371         }
372 
373         @Override
onSurfaceRedrawNeeded(SurfaceHolder holder)374         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
375             if (DEBUG) {
376                 Log.d(TAG, "onSurfaceRedrawNeeded");
377             }
378             super.onSurfaceRedrawNeeded(holder);
379 
380             drawFrame(true /* forceRedraw */);
381         }
382 
383         @RequiresApi(VERSION_CODES.O_MR1)
384         @Override
onComputeColors()385         public WallpaperColors onComputeColors() {
386             // It's OK to return null here.
387             return mCachedWallpaperColors;
388         }
389 
390         /**
391          * Invalidates the currently-drawn wallpaper image, causing the engine to reload the image from
392          * disk and draw the new wallpaper image.
393          */
invalidateAndRedrawWallpaper()394         public void invalidateAndRedrawWallpaper() {
395             // If a wallpaper load was already in flight, cancel it and restart a load in order to decode
396             // the new image.
397             if (mLoader != null) {
398                 mLoader.cancel(true /* mayInterruptIfRunning */);
399                 mLoader = null;
400             }
401 
402             loadWallpaper(true /* needsDraw */);
403         }
404 
drawFrame(boolean forceRedraw)405         void drawFrame(boolean forceRedraw) {
406             if (!mSurfaceValid) {
407                 return;
408             }
409 
410             Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(mDefaultDisplay);
411             int newRotation = mDefaultDisplay.getRotation();
412 
413             // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
414             // Call updateSurfaceSize -- it will only actually do the update if the dimensions
415             // should change
416             if (newRotation != mLastRotation) {
417                 // Update surface size (if necessary)
418                 if (!updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, true /* forDraw */)) {
419                     return;
420                 }
421                 mRotationAtLastSurfaceSizeUpdate = newRotation;
422                 mDisplayWidthAtLastSurfaceSizeUpdate = screenSize.x;
423                 mDisplayHeightAtLastSurfaceSizeUpdate = screenSize.y;
424             }
425             SurfaceHolder sh = getSurfaceHolder();
426             final Rect frame = sh.getSurfaceFrame();
427             final int dw = frame.width();
428             final int dh = frame.height();
429             boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
430                     || dh != mLastSurfaceHeight;
431 
432             boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation
433                     || forceRedraw;
434             if (!redrawNeeded && !mOffsetsChanged) {
435                 if (DEBUG) {
436                     Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
437                             + "and offsets have not changed.");
438                 }
439                 return;
440             }
441             mLastRotation = newRotation;
442 
443             // Load bitmap if its null and we're not using hardware acceleration.
444             if ((mIsHardwareAccelerated && !mIsOpenGlTextureLoaded) // Using OpenGL but texture not loaded
445                     || (!mIsHardwareAccelerated && mBackground == null)) { // Draw with Canvas but no bitmap
446                 if (DEBUG) {
447                     Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = "
448                             + mBackground + ", " + ((mBackground == null) ? 0 : mBackground.getWidth()) + ", "
449                             + ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " + dw + ", " + dh);
450                 }
451                 loadWallpaper(true /* needDraw */);
452                 if (DEBUG) {
453                     Log.d(TAG, "Reloading, resuming draw later");
454                 }
455                 return;
456             }
457 
458             // Center the scaled image
459             mScale = Math.max(1f, Math.max(dw / (float) mBackgroundWidth,
460                     dh / (float) mBackgroundHeight));
461             final int availw = dw - (int) (mBackgroundWidth * mScale);
462             final int availh = dh - (int) (mBackgroundHeight * mScale);
463             int xPixels = availw / 2;
464             int yPixels = availh / 2;
465 
466             // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
467             // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
468             // will remain unchanged
469             final int availwUnscaled = dw - mBackgroundWidth;
470             final int availhUnscaled = dh - mBackgroundHeight;
471             if (availwUnscaled < 0) {
472                 xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
473             }
474             if (availhUnscaled < 0) {
475                 yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);
476             }
477 
478             mOffsetsChanged = false;
479             if (surfaceDimensionsChanged) {
480                 mLastSurfaceWidth = dw;
481                 mLastSurfaceHeight = dh;
482             }
483             if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
484                 if (DEBUG) {
485                     Log.d(TAG, "Suppressed drawFrame since the image has not "
486                             + "actually moved an integral number of pixels.");
487                 }
488                 return;
489             }
490             mLastXTranslation = xPixels;
491             mLastYTranslation = yPixels;
492 
493             if (DEBUG) {
494                 Log.d(TAG, "Redrawing wallpaper");
495             }
496 
497             if (mIsHardwareAccelerated) {
498                 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
499                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
500                 } else {
501                     // If OpenGL drawing was successful, then we can safely discard a reference to the
502                     // wallpaper bitmap to save memory (since a copy has already been loaded into an OpenGL
503                     // texture).
504                     mBackground = null;
505                 }
506             } else {
507                 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
508             }
509         }
510 
511         /**
512          * Loads the wallpaper on background thread and schedules updating the surface frame,
513          * and if {@param needsDraw} is set also draws a frame.
514          * <p>
515          * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
516          * the active request).
517          * <p>
518          * If {@param needsReset} is set also clears the cache in WallpaperManager first.
519          */
loadWallpaper(boolean needsDraw)520         private void loadWallpaper(boolean needsDraw) {
521             mNeedsDrawAfterLoadingWallpaper |= needsDraw;
522             if (mLoader != null) {
523                 if (DEBUG) {
524                     Log.d(TAG, "Skipping loadWallpaper, already in flight ");
525                 }
526                 return;
527             }
528             mLoader = new AsyncTask<Void, Void, Bitmap>() {
529                 @Override
530                 protected Bitmap doInBackground(Void... params) {
531                     Throwable exception = null;
532                     try {
533                         // Decode bitmap of rotating image wallpaper.
534                         String wallpaperFilePath = isPreview()
535                                 ? PREVIEW_WALLPAPER_FILE_PATH : ROTATING_WALLPAPER_FILE_PATH;
536                         Context context = isPreview() ? getApplicationContext()
537                                 : getApplicationContext().createDeviceProtectedStorageContext();
538                         FileInputStream fileInputStream = context.openFileInput(wallpaperFilePath);
539                         Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream);
540                         fileInputStream.close();
541                         return bitmap;
542                     } catch (RuntimeException | FileNotFoundException | OutOfMemoryError e) {
543                         Log.i(TAG, "couldn't decode stream: ", e);
544                         exception = e;
545                     } catch (IOException e) {
546                         Log.i(TAG, "couldn't close stream: ", e);
547                         exception = e;
548                     }
549 
550                     if (isCancelled()) {
551                         return null;
552                     }
553 
554                     if (exception != null) {
555                         // Note that if we do fail at this, and the default wallpaper can't
556                         // be loaded, we will go into a cycle.  Don't do a build where the
557                         // default wallpaper can't be loaded.
558                         Log.w(TAG, "Unable to load wallpaper!", exception);
559                         try {
560                             return ((BitmapDrawable) getFallbackDrawable()).getBitmap();
561                         } catch (OutOfMemoryError ex) {
562                             // now we're really screwed.
563                             Log.w(TAG, "Unable reset to default wallpaper!", ex);
564                         }
565 
566                         if (isCancelled()) {
567                             return null;
568                         }
569                     }
570                     return null;
571                 }
572 
573                 @Override
574                 protected void onPostExecute(Bitmap b) {
575                     mBackground = null;
576                     mBackgroundWidth = -1;
577                     mBackgroundHeight = -1;
578 
579                     if (b != null) {
580                         mBackground = b;
581                         mBackgroundWidth = mBackground.getWidth();
582                         mBackgroundHeight = mBackground.getHeight();
583 
584                         if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
585                             mCachedWallpaperColors = WallpaperColors.fromBitmap(mBackground);
586                             notifyColorsChanged();
587                         }
588                     }
589 
590                     if (DEBUG) {
591                         Log.d(TAG, "Wallpaper loaded: " + mBackground);
592                     }
593                     updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay,
594                             false /* forDraw */);
595                     if (mTexture != 0 && mEgl != null) {
596                         deleteTexture(mTexture);
597                     }
598                     // If background is absent (due to an error decoding the bitmap) then don't try to load
599                     // a texture.
600                     if (mEgl != null && mBackground != null) {
601                         mTexture = loadTexture(mBackground);
602                     }
603                     if (mNeedsDrawAfterLoadingWallpaper) {
604                         drawFrame(true /* forceRedraw */);
605                     }
606 
607                     mLoader = null;
608                     mNeedsDrawAfterLoadingWallpaper = false;
609                 }
610             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
611         }
612 
getFallbackDrawable()613         private Drawable getFallbackDrawable() {
614             Drawable drawable;
615             try {
616                 drawable = mWallpaperManager.getDrawable();
617             } catch (java.lang.Exception e) {
618                 // Work around Samsung bug where SecurityException is thrown if device is still using its
619                 // default wallpaper, and around Android 7.0 bug where SELinux issues can cause a perfectly
620                 // valid access of the current wallpaper to cause a failed Binder transaction manifest here
621                 // as a RuntimeException.
622                 drawable = mWallpaperManager.getBuiltInDrawable();
623             }
624             return drawable;
625         }
626 
627         @Override
dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)628         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
629             super.dump(prefix, fd, out, args);
630 
631             out.print(prefix);
632             out.println("ImageWallpaper.DrawableEngine:");
633             out.print(prefix);
634             out.print(" mBackground=");
635             out.print(mBackground);
636             out.print(" mBackgroundWidth=");
637             out.print(mBackgroundWidth);
638             out.print(" mBackgroundHeight=");
639             out.println(mBackgroundHeight);
640 
641             out.print(prefix);
642             out.print(" mLastRotation=");
643             out.print(mLastRotation);
644             out.print(" mLastSurfaceWidth=");
645             out.print(mLastSurfaceWidth);
646             out.print(" mLastSurfaceHeight=");
647             out.println(mLastSurfaceHeight);
648 
649             out.print(prefix);
650             out.print(" mXOffset=");
651             out.print(mXOffset);
652             out.print(" mYOffset=");
653             out.println(mYOffset);
654 
655             out.print(prefix);
656             out.print(" mVisible=");
657             out.print(mVisible);
658             out.print(" mOffsetsChanged=");
659             out.println(mOffsetsChanged);
660 
661             out.print(prefix);
662             out.print(" mLastXTranslation=");
663             out.print(mLastXTranslation);
664             out.print(" mLastYTranslation=");
665             out.print(mLastYTranslation);
666             out.print(" mScale=");
667             out.println(mScale);
668 
669             out.print(prefix);
670             out.print(" mLastRequestedWidth=");
671             out.print(mLastRequestedWidth);
672             out.print(" mLastRequestedHeight=");
673             out.println(mLastRequestedHeight);
674 
675             out.print(prefix);
676             out.println(" DisplayInfo at last updateSurfaceSize:");
677             out.print(prefix);
678             out.print("  rotation=");
679             out.print(mRotationAtLastSurfaceSizeUpdate);
680             out.print("  width=");
681             out.print(mDisplayWidthAtLastSurfaceSizeUpdate);
682             out.print("  height=");
683             out.println(mDisplayHeightAtLastSurfaceSizeUpdate);
684         }
685 
drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top)686         private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) {
687             Canvas c = sh.lockCanvas();
688             if (c != null) {
689                 try {
690                     if (DEBUG) {
691                         Log.d(TAG, "Redrawing: left=" + left + ", top=" + top);
692                     }
693 
694                     final float right = left + mBackgroundWidth * mScale;
695                     final float bottom = top + mBackgroundHeight * mScale;
696                     if (w < 0 || h < 0) {
697                         c.save();
698                         c.clipOutRect(left, top, right, bottom);
699                         c.drawColor(0xff000000);
700                         c.restore();
701                     }
702                     if (mBackground != null) {
703                         RectF dest = new RectF(left, top, right, bottom);
704                         // add a filter bitmap?
705                         c.drawBitmap(mBackground, null, dest, null);
706                     }
707                 } finally {
708                     sh.unlockCanvasAndPost(c);
709                 }
710             }
711         }
712 
drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top)713         private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
714 
715             mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
716 
717             final float right = left + mBackgroundWidth * mScale;
718             final float bottom = top + mBackgroundHeight * mScale;
719 
720             final Rect frame = sh.getSurfaceFrame();
721             final Matrix4f ortho = new Matrix4f();
722             ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
723 
724             final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
725 
726             final int attribPosition = GLES20.glGetAttribLocation(mProgram, "position");
727             final int attribTexCoords = GLES20.glGetAttribLocation(mProgram, "texCoords");
728             final int uniformTexture = GLES20.glGetUniformLocation(mProgram, "texture");
729             final int uniformProjection = GLES20.glGetUniformLocation(mProgram, "projection");
730 
731             checkGlError();
732 
733             GLES20.glViewport(0, 0, frame.width(), frame.height());
734             GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture);
735 
736             GLES20.glUseProgram(mProgram);
737             GLES20.glEnableVertexAttribArray(attribPosition);
738             GLES20.glEnableVertexAttribArray(attribTexCoords);
739             GLES20.glUniform1i(uniformTexture, 0);
740             GLES20.glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
741 
742             checkGlError();
743 
744             if (w > 0 || h > 0) {
745                 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
746                 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
747             }
748 
749             // drawQuad
750             triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
751             GLES20.glVertexAttribPointer(attribPosition, 3, GLES20.GL_FLOAT, false,
752                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
753 
754             triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
755             GLES20.glVertexAttribPointer(attribTexCoords, 3, GLES20.GL_FLOAT, false,
756                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
757 
758             GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
759 
760             boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
761             checkEglError();
762 
763             return status;
764         }
765 
createMesh(int left, int top, float right, float bottom)766         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
767             final float[] verticesData = {
768                     // X, Y, Z, U, V
769                     left, bottom, 0.0f, 0.0f, 1.0f,
770                     right, bottom, 0.0f, 1.0f, 1.0f,
771                     left, top, 0.0f, 0.0f, 0.0f,
772                     right, top, 0.0f, 1.0f, 0.0f,
773             };
774 
775             final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
776             final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
777                     ByteOrder.nativeOrder()).asFloatBuffer();
778             triangleVertices.put(verticesData).position(0);
779             return triangleVertices;
780         }
781 
loadTexture(Bitmap bitmap)782         private int loadTexture(Bitmap bitmap) {
783             mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
784 
785             int[] textures = new int[1];
786 
787             GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
788             GLES20.glGenTextures(1, textures, 0);
789             checkGlError();
790 
791             int texture = textures[0];
792             GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
793             checkGlError();
794 
795             GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
796             GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
797 
798             GLES20.glTexParameteri(
799                     GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
800             GLES20.glTexParameteri(
801                     GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
802 
803             GLUtils.texImage2D(
804                     GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, GLES20.GL_UNSIGNED_BYTE, 0);
805             checkGlError();
806 
807             mIsOpenGlTextureLoaded = true;
808 
809             return texture;
810         }
811 
buildProgram(String vertex, String fragment)812         private int buildProgram(String vertex, String fragment) {
813             int vertexShader = buildShader(vertex, GLES20.GL_VERTEX_SHADER);
814             if (vertexShader == 0) {
815                 return 0;
816             }
817 
818             int fragmentShader = buildShader(fragment, GLES20.GL_FRAGMENT_SHADER);
819             if (fragmentShader == 0) {
820                 return 0;
821             }
822 
823             int program = GLES20.glCreateProgram();
824             GLES20.glAttachShader(program, vertexShader);
825             GLES20.glAttachShader(program, fragmentShader);
826             GLES20.glLinkProgram(program);
827             checkGlError();
828 
829             GLES20.glDeleteShader(vertexShader);
830             GLES20.glDeleteShader(fragmentShader);
831 
832             int[] status = new int[1];
833             GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0);
834             if (status[0] != GLES20.GL_TRUE) {
835                 String error = GLES20.glGetProgramInfoLog(program);
836                 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
837                 GLES20.glDeleteProgram(program);
838                 return 0;
839             }
840 
841             return program;
842         }
843 
buildShader(String source, int type)844         private int buildShader(String source, int type) {
845             int shader = GLES20.glCreateShader(type);
846 
847             GLES20.glShaderSource(shader, source);
848             checkGlError();
849 
850             GLES20.glCompileShader(shader);
851             checkGlError();
852 
853             int[] status = new int[1];
854             GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0);
855             if (status[0] != GLES20.GL_TRUE) {
856                 String error = GLES20.glGetShaderInfoLog(shader);
857                 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
858                 GLES20.glDeleteShader(shader);
859                 return 0;
860             }
861 
862             return shader;
863         }
864 
checkEglError()865         private void checkEglError() {
866             int error = mEgl.eglGetError();
867             if (error != EGL10.EGL_SUCCESS) {
868                 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
869             }
870         }
871 
checkGlError()872         private void checkGlError() {
873             int error = GLES20.glGetError();
874             if (error != GLES20.GL_NO_ERROR) {
875                 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
876             }
877         }
878 
deleteTexture(int texture)879         private void deleteTexture(int texture) {
880             int[] textures = new int[1];
881             textures[0] = texture;
882 
883             mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
884             GLES20.glDeleteTextures(1, textures, 0);
885             mTexture = 0;
886         }
887 
finishGL(int texture, int program)888         private void finishGL(int texture, int program) {
889             if (mEgl == null) {
890                 return;
891             }
892 
893             mOpenGlContextCounter--;
894 
895             mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
896             deleteTexture(mTexture);
897             GLES20.glDeleteProgram(program);
898             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
899             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
900             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
901             if (mOpenGlContextCounter == 0) {
902                 mEgl.eglTerminate(mEglDisplay);
903             }
904 
905             mEgl = null;
906         }
907 
initGL(SurfaceHolder surfaceHolder)908         private boolean initGL(SurfaceHolder surfaceHolder) {
909             mEgl = (EGL10) EGLContext.getEGL();
910 
911             mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
912             if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
913                 throw new RuntimeException("eglGetDisplay failed "
914                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
915             }
916 
917             int[] version = new int[2];
918             if (!mEgl.eglInitialize(mEglDisplay, version)) {
919                 throw new RuntimeException("eglInitialize failed "
920                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
921             }
922 
923             mOpenGlContextCounter++;
924 
925             mEglConfig = chooseEglConfig();
926             if (mEglConfig == null) {
927                 throw new RuntimeException("eglConfig not initialized");
928             }
929 
930             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
931             if (mEglContext == EGL_NO_CONTEXT) {
932                 throw new RuntimeException("createContext failed "
933                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
934             }
935 
936             int attribs[] = {
937                     EGL10.EGL_WIDTH, 1,
938                     EGL10.EGL_HEIGHT, 1,
939                     EGL10.EGL_NONE
940             };
941             EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
942             mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
943 
944             int[] maxSize = new int[1];
945             Rect frame = surfaceHolder.getSurfaceFrame();
946             GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0);
947 
948             mEgl.eglMakeCurrent(
949                     mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
950             mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
951 
952             if (frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
953                 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
954                 mEgl.eglTerminate(mEglDisplay);
955                 Log.e(GL_LOG_TAG, "requested  texture size " + frame.width() + "x" + frame.height()
956                         + " exceeds the support maximum of " + maxSize[0] + "x" + maxSize[0]);
957                 return false;
958             }
959 
960             mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
961             if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
962                 int error = mEgl.eglGetError();
963                 if (error == EGL10.EGL_BAD_NATIVE_WINDOW || error == EGL10.EGL_BAD_ALLOC) {
964                     Log.e(GL_LOG_TAG, "createWindowSurface returned " + GLUtils.getEGLErrorString(error)
965                             + ".");
966                     return false;
967                 }
968                 throw new RuntimeException("createWindowSurface failed "
969                         + GLUtils.getEGLErrorString(error));
970             }
971 
972             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
973                 throw new RuntimeException("eglMakeCurrent failed "
974                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
975             }
976 
977             mProgram = buildProgram(S_SIMPLE_VS, S_SIMPLE_FS);
978             if (mBackground != null) {
979                 mTexture = loadTexture(mBackground);
980             }
981 
982             return true;
983         }
984 
985 
createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)986         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
987             int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
988             return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribList);
989         }
990 
chooseEglConfig()991         private EGLConfig chooseEglConfig() {
992             int[] configsCount = new int[1];
993             EGLConfig[] configs = new EGLConfig[1];
994             int[] configSpec = getConfig();
995             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
996                 throw new IllegalArgumentException("eglChooseConfig failed "
997                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
998             } else if (configsCount[0] > 0) {
999                 return configs[0];
1000             }
1001             return null;
1002         }
1003 
getConfig()1004         private int[] getConfig() {
1005             return new int[]{
1006                     EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1007                     EGL10.EGL_RED_SIZE, 8,
1008                     EGL10.EGL_GREEN_SIZE, 8,
1009                     EGL10.EGL_BLUE_SIZE, 8,
1010                     EGL10.EGL_ALPHA_SIZE, 0,
1011                     EGL10.EGL_DEPTH_SIZE, 0,
1012                     EGL10.EGL_STENCIL_SIZE, 0,
1013                     EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE,
1014                     EGL10.EGL_NONE
1015             };
1016         }
1017     }
1018 }
1019