• 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 android.annotation.SuppressLint;
19 import android.app.WallpaperManager;
20 import android.content.Context;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.drawable.BitmapDrawable;
24 import android.os.AsyncTask;
25 import android.os.ParcelFileDescriptor;
26 import android.util.Log;
27 
28 import com.android.wallpaper.R;
29 import com.android.wallpaper.asset.BitmapUtils;
30 import com.android.wallpaper.compat.BuildCompat;
31 import com.android.wallpaper.compat.WallpaperManagerCompat;
32 import com.android.wallpaper.model.WallpaperMetadata;
33 
34 import java.io.FileInputStream;
35 import java.io.FileNotFoundException;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 
42 /**
43  * Default implementation of {@link WallpaperRefresher} which refreshes wallpaper metadata
44  * asynchronously.
45  */
46 @SuppressLint("ServiceCast")
47 public class DefaultWallpaperRefresher implements WallpaperRefresher {
48     private static final String TAG = "DefaultWPRefresher";
49 
50     private final Context mAppContext;
51     private final WallpaperPreferences mWallpaperPreferences;
52     private final WallpaperManager mWallpaperManager;
53     private final LiveWallpaperStatusChecker mLiveWallpaperStatusChecker;
54     private final UserEventLogger mUserEventLogger;
55     private final Context mDeviceProtectedContext;
56 
57     /**
58      * @param context The application's context.
59      */
DefaultWallpaperRefresher(Context context)60     public DefaultWallpaperRefresher(Context context) {
61         mAppContext = context.getApplicationContext();
62 
63         Injector injector = InjectorProvider.getInjector();
64         mWallpaperPreferences = injector.getPreferences(mAppContext);
65         mLiveWallpaperStatusChecker = injector.getLiveWallpaperStatusChecker(mAppContext);
66         mUserEventLogger = injector.getUserEventLogger(mAppContext);
67 
68         // Retrieve WallpaperManager using Context#getSystemService instead of
69         // WallpaperManager#getInstance so it can be mocked out in test.
70         mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
71         mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext();
72     }
73 
74     @Override
refresh(RefreshListener listener)75     public void refresh(RefreshListener listener) {
76         GetWallpaperMetadataAsyncTask task = new GetWallpaperMetadataAsyncTask(listener);
77         task.execute();
78     }
79 
80     /**
81      * Retrieves the current wallpaper's thumbnail and metadata off the UI thread.
82      */
83     private class GetWallpaperMetadataAsyncTask extends
84             AsyncTask<Void, Void, List<WallpaperMetadata>> {
85         private final RefreshListener mListener;
86         private final WallpaperManagerCompat mWallpaperManagerCompat;
87 
88         private long mCurrentHomeWallpaperHashCode;
89         private long mCurrentLockWallpaperHashCode;
90         private String mSystemWallpaperPackageName;
91 
92         @SuppressLint("ServiceCast")
GetWallpaperMetadataAsyncTask(RefreshListener listener)93         public GetWallpaperMetadataAsyncTask(RefreshListener listener) {
94             mListener = listener;
95             mWallpaperManagerCompat =
96                     InjectorProvider.getInjector().getWallpaperManagerCompat(mAppContext);
97         }
98 
99         @Override
doInBackground(Void... unused)100         protected List<WallpaperMetadata> doInBackground(Void... unused) {
101             List<WallpaperMetadata> wallpaperMetadatas = new ArrayList<>();
102 
103             if (!isHomeScreenMetadataCurrent() || isHomeScreenAttributionsEmpty()) {
104                 mWallpaperPreferences.clearHomeWallpaperMetadata();
105                 setFallbackHomeScreenWallpaperMetadata();
106             }
107 
108             boolean isLockScreenWallpaperCurrentlySet = LockWallpaperStatusChecker
109                     .isLockWallpaperSet(mAppContext);
110 
111             if (!BuildCompat.isAtLeastN() || !isLockScreenWallpaperCurrentlySet) {
112                 // Return only home metadata if pre-N device or lock screen wallpaper is not explicitly set.
113                 wallpaperMetadatas.add(new WallpaperMetadata(
114                         mWallpaperPreferences.getHomeWallpaperAttributions(),
115                         mWallpaperPreferences.getHomeWallpaperActionUrl(),
116                         mWallpaperPreferences.getHomeWallpaperActionLabelRes(),
117                         mWallpaperPreferences.getHomeWallpaperActionIconRes(),
118                         mWallpaperPreferences.getHomeWallpaperCollectionId(),
119                         mWallpaperPreferences.getHomeWallpaperBackingFileName(),
120                         mWallpaperManager.getWallpaperInfo()));
121                 return wallpaperMetadatas;
122             }
123 
124             if (!isLockScreenMetadataCurrent() || isLockScreenAttributionsEmpty()) {
125                 mWallpaperPreferences.clearLockWallpaperMetadata();
126                 setFallbackLockScreenWallpaperMetadata();
127             }
128 
129             wallpaperMetadatas.add(new WallpaperMetadata(
130                     mWallpaperPreferences.getHomeWallpaperAttributions(),
131                     mWallpaperPreferences.getHomeWallpaperActionUrl(),
132                     mWallpaperPreferences.getHomeWallpaperActionLabelRes(),
133                     mWallpaperPreferences.getHomeWallpaperActionIconRes(),
134                     mWallpaperPreferences.getHomeWallpaperCollectionId(),
135                     mWallpaperPreferences.getHomeWallpaperBackingFileName(),
136                     mWallpaperManager.getWallpaperInfo()));
137 
138             wallpaperMetadatas.add(new WallpaperMetadata(
139                     mWallpaperPreferences.getLockWallpaperAttributions(),
140                     mWallpaperPreferences.getLockWallpaperActionUrl(),
141                     mWallpaperPreferences.getLockWallpaperActionLabelRes(),
142                     mWallpaperPreferences.getLockWallpaperActionIconRes(),
143                     mWallpaperPreferences.getLockWallpaperCollectionId(),
144                     mWallpaperPreferences.getLockWallpaperBackingFileName(),
145                     null /* wallpaperComponent */));
146 
147             return wallpaperMetadatas;
148         }
149 
150         @Override
onPostExecute(List<WallpaperMetadata> metadatas)151         protected void onPostExecute(List<WallpaperMetadata> metadatas) {
152             if (metadatas.size() > 2) {
153                 Log.e(TAG, "Got more than 2 WallpaperMetadata objects - only home and (optionally) lock "
154                         + "are permitted.");
155                 return;
156             }
157 
158             mListener.onRefreshed(metadatas.get(0), metadatas.size() > 1 ? metadatas.get(1) : null,
159                     mWallpaperPreferences.getWallpaperPresentationMode());
160         }
161 
162         /**
163          * Sets fallback wallpaper attributions to WallpaperPreferences when the saved metadata did not
164          * match the system wallpaper. For live wallpapers, loads the label (title) but for image
165          * wallpapers loads a generic title string.
166          */
setFallbackHomeScreenWallpaperMetadata()167         private void setFallbackHomeScreenWallpaperMetadata() {
168             android.app.WallpaperInfo wallpaperComponent = mWallpaperManager.getWallpaperInfo();
169             if (wallpaperComponent == null) { // Image wallpaper
170                 mWallpaperPreferences.setHomeWallpaperAttributions(
171                         Arrays.asList(mAppContext.getResources().getString(R.string.fallback_wallpaper_title)));
172 
173                 // Set wallpaper ID if at least N or set a hash code if an earlier version of Android.
174                 if (BuildCompat.isAtLeastN()) {
175                     mWallpaperPreferences.setHomeWallpaperManagerId(mWallpaperManagerCompat.getWallpaperId(
176                             WallpaperManagerCompat.FLAG_SYSTEM));
177                 } else {
178                     mWallpaperPreferences.setHomeWallpaperHashCode(getCurrentHomeWallpaperHashCode());
179                 }
180             } else { // Live wallpaper
181                 mWallpaperPreferences.setHomeWallpaperAttributions(Arrays.asList(
182                         wallpaperComponent.loadLabel(mAppContext.getPackageManager()).toString()));
183                 mWallpaperPreferences.setHomeWallpaperPackageName(mSystemWallpaperPackageName);
184             }
185             mWallpaperPreferences.setWallpaperPresentationMode(
186                     WallpaperPreferences.PRESENTATION_MODE_STATIC);
187         }
188 
189         /**
190          * Sets fallback lock screen wallpaper attributions to WallpaperPreferences. This should be
191          * called when the saved lock screen wallpaper metadata does not match the currently set lock
192          * screen wallpaper.
193          */
setFallbackLockScreenWallpaperMetadata()194         private void setFallbackLockScreenWallpaperMetadata() {
195             mWallpaperPreferences.setLockWallpaperAttributions(
196                     Arrays.asList(mAppContext.getResources().getString(R.string.fallback_wallpaper_title)));
197             mWallpaperPreferences.setLockWallpaperId(mWallpaperManagerCompat.getWallpaperId(
198                     WallpaperManagerCompat.FLAG_LOCK));
199         }
200 
201         /**
202          * Returns whether the home screen metadata saved in WallpaperPreferences corresponds to the
203          * current system wallpaper.
204          */
isHomeScreenMetadataCurrent()205         private boolean isHomeScreenMetadataCurrent() {
206             return (mWallpaperManager.getWallpaperInfo() == null
207                     || mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet())
208                     ? isHomeScreenImageWallpaperCurrent()
209                     : isHomeScreenLiveWallpaperCurrent();
210         }
211 
212         /**
213          * Returns whether the home screen attributions saved in WallpaperPreferences is empty.
214          */
isHomeScreenAttributionsEmpty()215         private boolean isHomeScreenAttributionsEmpty() {
216             List<String> homeScreenAttributions = mWallpaperPreferences.getHomeWallpaperAttributions();
217             return homeScreenAttributions.get(0) == null
218                     && homeScreenAttributions.get(1) == null
219                     && homeScreenAttributions.get(2) == null;
220         }
221 
getCurrentHomeWallpaperHashCode()222         private long getCurrentHomeWallpaperHashCode() {
223             if (mCurrentHomeWallpaperHashCode == 0) {
224                 if (mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) {
225 
226                     synchronized (RotatingWallpaperLockProvider.getInstance()) {
227                         Bitmap bitmap = null;
228                         try {
229                             FileInputStream fis =
230                                     mDeviceProtectedContext.openFileInput(
231                                             NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
232                             bitmap = BitmapFactory.decodeStream(fis);
233                             fis.close();
234                         } catch (FileNotFoundException e) {
235                             Log.e(TAG, "Rotating wallpaper file not found at path: "
236                                     + mDeviceProtectedContext.getFileStreamPath(
237                                             NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH),
238                                     e);
239                         } catch (IOException e) {
240                             Log.e(TAG, "IOException when closing FileInputStream " + e);
241                         }
242 
243                         if (bitmap != null) {
244                             mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(bitmap);
245                             mUserEventLogger.logDailyWallpaperDecodes(true);
246                         } else {
247                             // If an error occurred decoding the stream then we should just assume the current
248                             // home wallpaper remained intact.
249                             mCurrentHomeWallpaperHashCode = mWallpaperPreferences.getHomeWallpaperHashCode();
250                             mUserEventLogger.logDailyWallpaperDecodes(false);
251                         }
252                     }
253                 } else {
254                     BitmapDrawable wallpaperDrawable = (BitmapDrawable) mWallpaperManagerCompat.getDrawable();
255                     Bitmap wallpaperBitmap = wallpaperDrawable.getBitmap();
256                     mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap);
257 
258                     // Manually request that WallpaperManager loses its reference to the current wallpaper
259                     // bitmap, which can occupy a large memory allocation for the lifetime of the app.
260                     mWallpaperManager.forgetLoadedWallpaper();
261                 }
262             }
263             return mCurrentHomeWallpaperHashCode;
264         }
265 
getCurrentLockWallpaperHashCode()266         private long getCurrentLockWallpaperHashCode() {
267             if (mCurrentLockWallpaperHashCode == 0
268                     && LockWallpaperStatusChecker.isLockWallpaperSet(mAppContext)) {
269                 Bitmap wallpaperBitmap = getLockWallpaperBitmap();
270                 mCurrentLockWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap);
271             }
272             return mCurrentLockWallpaperHashCode;
273         }
274 
275         /**
276          * Returns the lock screen wallpaper currently set on the device as a Bitmap, or null if no
277          * lock screen wallpaper is set.
278          */
getLockWallpaperBitmap()279         private Bitmap getLockWallpaperBitmap() {
280             Bitmap lockBitmap = null;
281 
282             ParcelFileDescriptor pfd = mWallpaperManagerCompat.getWallpaperFile(
283                     WallpaperManagerCompat.FLAG_LOCK);
284             // getWallpaperFile returns null if the lock screen isn't explicitly set, so need this
285             // check.
286             if (pfd != null) {
287                 InputStream fileStream = null;
288                 try {
289                     fileStream = new FileInputStream(pfd.getFileDescriptor());
290                     lockBitmap = BitmapFactory.decodeStream(fileStream);
291                     pfd.close();
292                     return lockBitmap;
293                 } catch (IOException e) {
294                     Log.e(TAG, "IO exception when closing the file descriptor.");
295                 } finally {
296                     if (fileStream != null) {
297                         try {
298                             fileStream.close();
299                         } catch (IOException e) {
300                             Log.e(TAG, "IO exception when closing input stream for lock screen WP.");
301                         }
302                     }
303                 }
304             }
305 
306             return lockBitmap;
307         }
308 
309         /**
310          * Returns whether the image wallpaper set to the system matches the metadata in
311          * WallpaperPreferences.
312          */
isHomeScreenImageWallpaperCurrent()313         private boolean isHomeScreenImageWallpaperCurrent() {
314             long savedBitmapHash = mWallpaperPreferences.getHomeWallpaperHashCode();
315 
316             // Use WallpaperManager IDs to check same-ness of image wallpaper on N+ versions of Android
317             // only when there is no saved bitmap hash code (which could be leftover from a previous build
318             // of the app that did not use wallpaper IDs).
319             if (BuildCompat.isAtLeastN() && savedBitmapHash == 0) {
320                 return mWallpaperPreferences.getHomeWallpaperManagerId()
321                         == mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_SYSTEM);
322             }
323 
324             return savedBitmapHash == getCurrentHomeWallpaperHashCode();
325         }
326 
327         /**
328          * Returns whether the live wallpaper set to the system's home screen matches the metadata in
329          * WallpaperPreferences.
330          */
isHomeScreenLiveWallpaperCurrent()331         private boolean isHomeScreenLiveWallpaperCurrent() {
332             mSystemWallpaperPackageName = mWallpaperManager.getWallpaperInfo().getPackageName();
333             String homeWallpaperPackageName = mWallpaperPreferences.getHomeWallpaperPackageName();
334             return mSystemWallpaperPackageName.equals(homeWallpaperPackageName);
335         }
336 
337         /**
338          * Returns whether the lock screen metadata saved in WallpaperPreferences corresponds to the
339          * current lock screen wallpaper.
340          */
isLockScreenMetadataCurrent()341         private boolean isLockScreenMetadataCurrent() {
342             // Check for lock wallpaper image same-ness only when there is no stored lock wallpaper hash
343             // code. Otherwise if there is a lock wallpaper hash code stored in
344             // {@link WallpaperPreferences}, then check hash codes.
345             long savedLockWallpaperHash = mWallpaperPreferences.getLockWallpaperHashCode();
346 
347             return (savedLockWallpaperHash == 0)
348                     ? mWallpaperPreferences.getLockWallpaperId()
349                     == mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK)
350                     : savedLockWallpaperHash == getCurrentLockWallpaperHashCode();
351         }
352 
353         /**
354          * Returns whether the lock screen attributions saved in WallpaperPreferences are empty.
355          */
isLockScreenAttributionsEmpty()356         private boolean isLockScreenAttributionsEmpty() {
357             List<String> attributions = mWallpaperPreferences.getLockWallpaperAttributions();
358             return attributions.get(0) == null
359                     && attributions.get(1) == null
360                     && attributions.get(2) == null;
361         }
362     }
363 }
364