• 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.backup;
17 
18 import android.annotation.SuppressLint;
19 import android.app.WallpaperManager;
20 import android.app.job.JobInfo;
21 import android.app.job.JobParameters;
22 import android.app.job.JobScheduler;
23 import android.app.job.JobService;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.graphics.Bitmap;
27 import android.graphics.BitmapFactory;
28 import android.graphics.drawable.BitmapDrawable;
29 import android.graphics.drawable.Drawable;
30 import android.os.ParcelFileDescriptor;
31 import android.util.Log;
32 
33 import androidx.annotation.Nullable;
34 import androidx.annotation.VisibleForTesting;
35 
36 import com.android.wallpaper.asset.BitmapUtils;
37 import com.android.wallpaper.compat.WallpaperManagerCompat;
38 import com.android.wallpaper.module.Injector;
39 import com.android.wallpaper.module.InjectorProvider;
40 import com.android.wallpaper.module.JobSchedulerJobIds;
41 import com.android.wallpaper.module.WallpaperPreferences;
42 import com.android.wallpaper.util.DiskBasedLogger;
43 
44 import java.io.FileInputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 
48 /**
49  * {@link android.app.job.JobScheduler} job for generating missing hash codes for static wallpapers
50  * on N+ devices.
51  */
52 @SuppressLint("ServiceCast")
53 public class MissingHashCodeGeneratorJobService extends JobService {
54 
55     private static final String TAG = "MissingHashCodeGenerato"; // max 23 characters
56 
57     private Thread mWorkerThread;
58 
schedule(Context context)59     public static void schedule(Context context) {
60         JobScheduler scheduler = context.getSystemService(JobScheduler.class);
61         JobInfo newJob = new JobInfo.Builder(
62                 JobSchedulerJobIds.JOB_ID_GENERATE_MISSING_HASH_CODES,
63                 new ComponentName(context, MissingHashCodeGeneratorJobService.class))
64                 .setMinimumLatency(0)
65                 .setPersisted(true)
66                 .build();
67         scheduler.schedule(newJob);
68     }
69 
70     @Override
onStartJob(JobParameters jobParameters)71     public boolean onStartJob(JobParameters jobParameters) {
72         Context context = getApplicationContext();
73 
74         // Retrieve WallpaperManager using Context#getSystemService instead of
75         // WallpaperManager#getInstance so it can be mocked out in test.
76         final WallpaperManager wallpaperManager = (WallpaperManager) context.getSystemService(
77                 Context.WALLPAPER_SERVICE);
78 
79         // Generate missing hash codes on a plain worker thread because we need to do some
80         // long-running disk I/O and can call #jobFinished from a background thread.
81         mWorkerThread = new Thread(new Runnable() {
82             @Override
83             public void run() {
84                 Injector injector = InjectorProvider.getInjector();
85                 WallpaperManagerCompat wallpaperManagerCompat = injector.getWallpaperManagerCompat(
86                         context);
87                 WallpaperPreferences wallpaperPreferences = injector.getPreferences(context);
88 
89                 boolean isLiveWallpaperSet = wallpaperManager.getWallpaperInfo() != null;
90 
91                 // Generate and set a home wallpaper hash code if there's no live wallpaper set
92                 // and no hash code stored already for the home wallpaper.
93                 if (!isLiveWallpaperSet && wallpaperPreferences.getHomeWallpaperHashCode() == 0) {
94                     wallpaperManager.forgetLoadedWallpaper();
95 
96                     Drawable wallpaperDrawable = wallpaperManagerCompat.getDrawable();
97                     // No work to do if the drawable returned is null due to an underlying
98                     // platform issue -- being extra defensive with this check due to instability
99                     // and variability of underlying platform.
100                     if (wallpaperDrawable == null) {
101                         DiskBasedLogger.e(
102                                 TAG,
103                                 "WallpaperManager#getDrawable returned null and there's no live "
104                                         + "wallpaper set",
105                                 context
106                         );
107                         jobFinished(jobParameters, false /* needsReschedule */);
108                         return;
109                     }
110 
111                     Bitmap bitmap = ((BitmapDrawable) wallpaperDrawable).getBitmap();
112                     long homeBitmapHash = BitmapUtils.generateHashCode(bitmap);
113 
114                     wallpaperPreferences.setHomeWallpaperHashCode(homeBitmapHash);
115                 }
116 
117                 // Generate and set a lock wallpaper hash code if there's none saved.
118                 if (wallpaperPreferences.getLockWallpaperHashCode() == 0) {
119                     ParcelFileDescriptor parcelFd =
120                             wallpaperManagerCompat.getWallpaperFile(
121                                     WallpaperManagerCompat.FLAG_LOCK);
122                     boolean isLockWallpaperSet = parcelFd != null;
123 
124                     // Copy the home wallpaper's hash code to lock if there's no distinct lock
125                     // wallpaper set.
126                     if (!isLockWallpaperSet) {
127                         wallpaperPreferences.setLockWallpaperHashCode(
128                                 wallpaperPreferences.getHomeWallpaperHashCode());
129                         mWorkerThread = null;
130                         jobFinished(jobParameters, false /* needsReschedule */);
131                         return;
132                     }
133 
134                     // Otherwise, generate and set the distinct lock wallpaper image's hash code.
135                     Bitmap lockBitmap = null;
136                     InputStream fileStream = null;
137                     try {
138                         fileStream = new FileInputStream(parcelFd.getFileDescriptor());
139                         lockBitmap = BitmapFactory.decodeStream(fileStream);
140                         parcelFd.close();
141                     } catch (IOException e) {
142                         Log.e(TAG, "IO exception when closing the file descriptor.", e);
143                     } finally {
144                         if (fileStream != null) {
145                             try {
146                                 fileStream.close();
147                             } catch (IOException e) {
148                                 Log.e(TAG,
149                                         "IO exception when closing input stream for lock screen "
150                                                 + "wallpaper.",
151                                         e);
152                             }
153                         }
154                     }
155 
156                     if (lockBitmap != null) {
157                         wallpaperPreferences.setLockWallpaperHashCode(
158                                 BitmapUtils.generateHashCode(lockBitmap));
159                     }
160                     mWorkerThread = null;
161 
162                     jobFinished(jobParameters, false /* needsReschedule */);
163                 }
164             }
165         });
166 
167         mWorkerThread.start();
168 
169         // Return true to indicate that this JobService needs to process work on a separate thread.
170         return true;
171     }
172 
173     @Override
onStopJob(JobParameters jobParameters)174     public boolean onStopJob(JobParameters jobParameters) {
175         // This job has no special execution parameters (i.e., network capability, device idle or
176         // charging), so Android should never call this method to stop the execution of this job
177         // early. Return "false" to indicate that this job should not be rescheduled when it's
178         // stopped because we have to provide an implementation of this method.
179         return false;
180     }
181 
182     @Nullable
183     @VisibleForTesting
getWorkerThread()184         /* package */ Thread getWorkerThread() {
185         return mWorkerThread;
186     }
187 }
188