OSDN Git Service

f6f5341ff0a99eda46510b2d282b08f0f7ee7119
[android-x86/frameworks-base.git] / services / core / java / com / android / server / wallpaper / WallpaperManagerService.java
1 /*
2  * Copyright (C) 2008 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
17 package com.android.server.wallpaper;
18
19 import static android.app.WallpaperManager.FLAG_LOCK;
20 import static android.app.WallpaperManager.FLAG_SYSTEM;
21 import static android.os.ParcelFileDescriptor.MODE_CREATE;
22 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
23 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
24 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
25 import static android.view.Display.DEFAULT_DISPLAY;
26 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
27
28 import android.annotation.NonNull;
29 import android.app.ActivityManager;
30 import android.app.AppGlobals;
31 import android.app.AppOpsManager;
32 import android.app.IWallpaperManager;
33 import android.app.IWallpaperManagerCallback;
34 import android.app.PendingIntent;
35 import android.app.UserSwitchObserver;
36 import android.app.WallpaperColors;
37 import android.app.WallpaperInfo;
38 import android.app.WallpaperManager;
39 import android.app.admin.DevicePolicyManager;
40 import android.app.backup.WallpaperBackupHelper;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.ServiceConnection;
47 import android.content.pm.IPackageManager;
48 import android.content.pm.PackageManager;
49 import android.content.pm.PackageManager.NameNotFoundException;
50 import android.content.pm.ResolveInfo;
51 import android.content.pm.ServiceInfo;
52 import android.content.pm.UserInfo;
53 import android.content.res.Resources;
54 import android.graphics.Bitmap;
55 import android.graphics.BitmapFactory;
56 import android.graphics.BitmapRegionDecoder;
57 import android.graphics.Color;
58 import android.graphics.Point;
59 import android.graphics.Rect;
60 import android.os.Binder;
61 import android.os.Bundle;
62 import android.os.Environment;
63 import android.os.FileObserver;
64 import android.os.FileUtils;
65 import android.os.Handler;
66 import android.os.IBinder;
67 import android.os.IInterface;
68 import android.os.IRemoteCallback;
69 import android.os.ParcelFileDescriptor;
70 import android.os.Process;
71 import android.os.RemoteCallbackList;
72 import android.os.RemoteException;
73 import android.os.SELinux;
74 import android.os.ServiceManager;
75 import android.os.SystemClock;
76 import android.os.UserHandle;
77 import android.os.UserManager;
78 import android.service.wallpaper.IWallpaperConnection;
79 import android.service.wallpaper.IWallpaperEngine;
80 import android.service.wallpaper.IWallpaperService;
81 import android.service.wallpaper.WallpaperService;
82 import android.system.ErrnoException;
83 import android.system.Os;
84 import android.util.EventLog;
85 import android.util.Slog;
86 import android.util.SparseArray;
87 import android.util.Xml;
88 import android.view.Display;
89 import android.view.IWindowManager;
90 import android.view.WindowManager;
91
92 import com.android.internal.R;
93 import com.android.internal.content.PackageMonitor;
94 import com.android.internal.os.BackgroundThread;
95 import com.android.internal.util.DumpUtils;
96 import com.android.internal.util.FastXmlSerializer;
97 import com.android.internal.util.JournaledFile;
98 import com.android.server.EventLogTags;
99 import com.android.server.FgThread;
100 import com.android.server.SystemService;
101
102 import libcore.io.IoUtils;
103
104 import org.xmlpull.v1.XmlPullParser;
105 import org.xmlpull.v1.XmlPullParserException;
106 import org.xmlpull.v1.XmlSerializer;
107
108 import java.io.BufferedOutputStream;
109 import java.io.File;
110 import java.io.FileDescriptor;
111 import java.io.FileInputStream;
112 import java.io.FileNotFoundException;
113 import java.io.FileOutputStream;
114 import java.io.IOException;
115 import java.io.InputStream;
116 import java.io.PrintWriter;
117 import java.nio.charset.StandardCharsets;
118 import java.util.ArrayList;
119 import java.util.Arrays;
120 import java.util.List;
121 import java.util.Objects;
122
123 public class WallpaperManagerService extends IWallpaperManager.Stub {
124     static final String TAG = "WallpaperManagerService";
125     static final boolean DEBUG = false;
126     static final boolean DEBUG_LIVE = DEBUG || true;
127
128     public static class Lifecycle extends SystemService {
129         private WallpaperManagerService mService;
130
131         public Lifecycle(Context context) {
132             super(context);
133         }
134
135         @Override
136         public void onStart() {
137             mService = new WallpaperManagerService(getContext());
138             publishBinderService(Context.WALLPAPER_SERVICE, mService);
139         }
140
141         @Override
142         public void onBootPhase(int phase) {
143             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
144                 mService.systemReady();
145             } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
146                 mService.switchUser(UserHandle.USER_SYSTEM, null);
147             }
148         }
149
150         @Override
151         public void onUnlockUser(int userHandle) {
152             mService.onUnlockUser(userHandle);
153         }
154     }
155
156     final Object mLock = new Object();
157
158     /**
159      * Minimum time between crashes of a wallpaper service for us to consider
160      * restarting it vs. just reverting to the static wallpaper.
161      */
162     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
163     static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
164     static final String WALLPAPER = "wallpaper_orig";
165     static final String WALLPAPER_CROP = "wallpaper";
166     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
167     static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
168     static final String WALLPAPER_INFO = "wallpaper_info.xml";
169
170     // All the various per-user state files we need to be aware of
171     static final String[] sPerUserFiles = new String[] {
172         WALLPAPER, WALLPAPER_CROP,
173         WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
174         WALLPAPER_INFO
175     };
176
177     /**
178      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
179      * that the wallpaper has changed. The CREATE is triggered when there is no
180      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
181      * every time the wallpaper is changed.
182      */
183     private class WallpaperObserver extends FileObserver {
184
185         final int mUserId;
186         final WallpaperData mWallpaper;
187         final File mWallpaperDir;
188         final File mWallpaperFile;
189         final File mWallpaperLockFile;
190
191         public WallpaperObserver(WallpaperData wallpaper) {
192             super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
193                     CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
194             mUserId = wallpaper.userId;
195             mWallpaperDir = getWallpaperDir(wallpaper.userId);
196             mWallpaper = wallpaper;
197             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
198             mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
199         }
200
201         private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
202             WallpaperData wallpaper = null;
203             synchronized (mLock) {
204                 if (lockChanged) {
205                     wallpaper = mLockWallpaperMap.get(mUserId);
206                 }
207                 if (wallpaper == null) {
208                     // no lock-specific wallpaper exists, or sys case, handled together
209                     wallpaper = mWallpaperMap.get(mUserId);
210                 }
211             }
212             return (wallpaper != null) ? wallpaper : mWallpaper;
213         }
214
215         @Override
216         public void onEvent(int event, String path) {
217             if (path == null) {
218                 return;
219             }
220             final boolean moved = (event == MOVED_TO);
221             final boolean written = (event == CLOSE_WRITE || moved);
222             final File changedFile = new File(mWallpaperDir, path);
223
224             // System and system+lock changes happen on the system wallpaper input file;
225             // lock-only changes happen on the dedicated lock wallpaper input file
226             final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
227             final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
228             int notifyColorsWhich = 0;
229             WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
230
231             if (DEBUG) {
232                 Slog.v(TAG, "Wallpaper file change: evt=" + event
233                         + " path=" + path
234                         + " sys=" + sysWallpaperChanged
235                         + " lock=" + lockWallpaperChanged
236                         + " imagePending=" + wallpaper.imageWallpaperPending
237                         + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending)
238                         + " written=" + written);
239             }
240
241             if (moved && lockWallpaperChanged) {
242                 // We just migrated sys -> lock to preserve imagery for an impending
243                 // new system-only wallpaper.  Tell keyguard about it and make sure it
244                 // has the right SELinux label.
245                 if (DEBUG) {
246                     Slog.i(TAG, "Sys -> lock MOVED_TO");
247                 }
248                 SELinux.restorecon(changedFile);
249                 notifyLockWallpaperChanged();
250                 notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);
251                 return;
252             }
253
254             synchronized (mLock) {
255                 if (sysWallpaperChanged || lockWallpaperChanged) {
256                     notifyCallbacksLocked(wallpaper);
257                     if (wallpaper.wallpaperComponent == null
258                             || event != CLOSE_WRITE // includes the MOVED_TO case
259                             || wallpaper.imageWallpaperPending) {
260                         if (written) {
261                             // The image source has finished writing the source image,
262                             // so we now produce the crop rect (in the background), and
263                             // only publish the new displayable (sub)image as a result
264                             // of that work.
265                             if (DEBUG) {
266                                 Slog.v(TAG, "Wallpaper written; generating crop");
267                             }
268                             SELinux.restorecon(changedFile);
269                             if (moved) {
270                                 // This is a restore, so generate the crop using any just-restored new
271                                 // crop guidelines, making sure to preserve our local dimension hints.
272                                 // We also make sure to reapply the correct SELinux label.
273                                 if (DEBUG) {
274                                     Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
275                                 }
276                                 loadSettingsLocked(wallpaper.userId, true);
277                             }
278                             generateCrop(wallpaper);
279                             if (DEBUG) {
280                                 Slog.v(TAG, "Crop done; invoking completion callback");
281                             }
282                             wallpaper.imageWallpaperPending = false;
283                             if (wallpaper.setComplete != null) {
284                                 try {
285                                     wallpaper.setComplete.onWallpaperChanged();
286                                 } catch (RemoteException e) {
287                                     // if this fails we don't really care; the setting app may just
288                                     // have crashed and that sort of thing is a fact of life.
289                                 }
290                             }
291                             if (sysWallpaperChanged) {
292                                 // If this was the system wallpaper, rebind...
293                                 bindWallpaperComponentLocked(mImageWallpaper, true,
294                                         false, wallpaper, null);
295                                 notifyColorsWhich |= FLAG_SYSTEM;
296                             }
297                             if (lockWallpaperChanged
298                                     || (wallpaper.whichPending & FLAG_LOCK) != 0) {
299                                 if (DEBUG) {
300                                     Slog.i(TAG, "Lock-relevant wallpaper changed");
301                                 }
302                                 // either a lock-only wallpaper commit or a system+lock event.
303                                 // if it's system-plus-lock we need to wipe the lock bookkeeping;
304                                 // we're falling back to displaying the system wallpaper there.
305                                 if (!lockWallpaperChanged) {
306                                     mLockWallpaperMap.remove(wallpaper.userId);
307                                 }
308                                 // and in any case, tell keyguard about it
309                                 notifyLockWallpaperChanged();
310                                 notifyColorsWhich |= FLAG_LOCK;
311                             }
312
313                             saveSettingsLocked(wallpaper.userId);
314                         }
315                     }
316                 }
317             }
318
319             // Outside of the lock since it will synchronize itself
320             if (notifyColorsWhich != 0) {
321                 notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
322             }
323         }
324     }
325
326     void notifyLockWallpaperChanged() {
327         final IWallpaperManagerCallback cb = mKeyguardListener;
328         if (cb != null) {
329             try {
330                 cb.onWallpaperChanged();
331             } catch (RemoteException e) {
332                 // Oh well it went away; no big deal
333             }
334         }
335     }
336
337     private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
338         boolean needsExtraction;
339         synchronized (mLock) {
340             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
341                     mColorsChangedListeners.get(wallpaper.userId);
342             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
343                     mColorsChangedListeners.get(UserHandle.USER_ALL);
344             // No-op until someone is listening to it.
345             if (emptyCallbackList(currentUserColorListeners)  &&
346                     emptyCallbackList(userAllColorListeners)) {
347                 return;
348             }
349
350             if (DEBUG) {
351                 Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
352             }
353
354             needsExtraction = wallpaper.primaryColors == null;
355         }
356
357         // Let's notify the current values, it's fine if it's null, it just means
358         // that we don't know yet.
359         notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
360
361         if (needsExtraction) {
362             extractColors(wallpaper);
363             synchronized (mLock) {
364                 // Don't need to notify if nothing changed.
365                 if (wallpaper.primaryColors == null) {
366                     return;
367                 }
368             }
369             notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
370         }
371     }
372
373     private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
374         return (list == null || list.getRegisteredCallbackCount() == 0);
375     }
376
377     private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
378             int userId) {
379         final IWallpaperManagerCallback keyguardListener;
380         final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
381         synchronized (mLock) {
382             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
383                     mColorsChangedListeners.get(userId);
384             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
385                     mColorsChangedListeners.get(UserHandle.USER_ALL);
386             keyguardListener = mKeyguardListener;
387
388             if (currentUserColorListeners != null) {
389                 final int count = currentUserColorListeners.beginBroadcast();
390                 for (int i = 0; i < count; i++) {
391                     colorListeners.add(currentUserColorListeners.getBroadcastItem(i));
392                 }
393                 currentUserColorListeners.finishBroadcast();
394             }
395
396             if (userAllColorListeners != null) {
397                 final int count = userAllColorListeners.beginBroadcast();
398                 for (int i = 0; i < count; i++) {
399                     colorListeners.add(userAllColorListeners.getBroadcastItem(i));
400                 }
401                 userAllColorListeners.finishBroadcast();
402             }
403         }
404
405         final int count = colorListeners.size();
406         for (int i = 0; i < count; i++) {
407             try {
408                 colorListeners.get(i).onWallpaperColorsChanged(wallpaperColors, which, userId);
409             } catch (RemoteException e) {
410                 // Callback is gone, it's not necessary to unregister it since
411                 // RemoteCallbackList#getBroadcastItem will take care of it.
412             }
413         }
414
415         if (keyguardListener != null) {
416             try {
417                 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
418             } catch (RemoteException e) {
419                 // Oh well it went away; no big deal
420             }
421         }
422     }
423
424     /**
425      * We can easily extract colors from an ImageWallpaper since it's only a bitmap.
426      * In this case, using the crop is more than enough. Live wallpapers are just ignored.
427      *
428      * @param wallpaper a wallpaper representation
429      */
430     private void extractColors(WallpaperData wallpaper) {
431         String cropFile = null;
432         int wallpaperId;
433
434         synchronized (mLock) {
435             // Not having a wallpaperComponent means it's a lock screen wallpaper.
436             final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
437                     || wallpaper.wallpaperComponent == null;
438             if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) {
439                 cropFile = wallpaper.cropFile.getAbsolutePath();
440             }
441             wallpaperId = wallpaper.wallpaperId;
442         }
443
444         WallpaperColors colors = null;
445         if (cropFile != null) {
446             Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
447             if (bitmap != null) {
448                 colors = WallpaperColors.fromBitmap(bitmap);
449                 bitmap.recycle();
450             }
451         }
452
453         if (colors == null) {
454             Slog.w(TAG, "Cannot extract colors because wallpaper could not be read.");
455             return;
456         }
457
458         synchronized (mLock) {
459             if (wallpaper.wallpaperId == wallpaperId) {
460                 wallpaper.primaryColors = colors;
461                 // Now that we have the colors, let's save them into the xml
462                 // to avoid having to run this again.
463                 saveSettingsLocked(wallpaper.userId);
464             } else {
465                 Slog.w(TAG, "Not setting primary colors since wallpaper changed");
466             }
467         }
468     }
469
470     /**
471      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
472      * for display.
473      */
474     private void generateCrop(WallpaperData wallpaper) {
475         boolean success = false;
476
477         Rect cropHint = new Rect(wallpaper.cropHint);
478
479         if (DEBUG) {
480             Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
481                     + Integer.toHexString(wallpaper.whichPending)
482                     + " to " + wallpaper.cropFile.getName()
483                     + " crop=(" + cropHint.width() + 'x' + cropHint.height()
484                     + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
485         }
486
487         // Analyse the source; needed in multiple cases
488         BitmapFactory.Options options = new BitmapFactory.Options();
489         options.inJustDecodeBounds = true;
490         BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
491         if (options.outWidth <= 0 || options.outHeight <= 0) {
492             Slog.w(TAG, "Invalid wallpaper data");
493             success = false;
494         } else {
495             boolean needCrop = false;
496             boolean needScale = false;
497
498             // Empty crop means use the full image
499             if (cropHint.isEmpty()) {
500                 cropHint.left = cropHint.top = 0;
501                 cropHint.right = options.outWidth;
502                 cropHint.bottom = options.outHeight;
503             } else {
504                 // force the crop rect to lie within the measured bounds
505                 cropHint.offset(
506                         (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
507                         (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
508
509                 // If the crop hint was larger than the image we just overshot. Patch things up.
510                 if (cropHint.left < 0) {
511                     cropHint.left = 0;
512                 }
513                 if (cropHint.top < 0) {
514                     cropHint.top = 0;
515                 }
516
517                 // Don't bother cropping if what we're left with is identity
518                 needCrop = (options.outHeight > cropHint.height()
519                         || options.outWidth > cropHint.width());
520             }
521
522             // scale if the crop height winds up not matching the recommended metrics
523             needScale = (wallpaper.height != cropHint.height());
524
525             if (DEBUG) {
526                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
527                 Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
528                 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
529                 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
530             }
531
532             if (!needCrop && !needScale) {
533                 // Simple case:  the nominal crop fits what we want, so we take
534                 // the whole thing and just copy the image file directly.
535                 if (DEBUG) {
536                     Slog.v(TAG, "Null crop of new wallpaper; copying");
537                 }
538                 success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
539                 if (!success) {
540                     wallpaper.cropFile.delete();
541                     // TODO: fall back to default wallpaper in this case
542                 }
543             } else {
544                 // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
545                 FileOutputStream f = null;
546                 BufferedOutputStream bos = null;
547                 try {
548                     BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
549                             wallpaper.wallpaperFile.getAbsolutePath(), false);
550
551                     // This actually downsamples only by powers of two, but that's okay; we do
552                     // a proper scaling blit later.  This is to minimize transient RAM use.
553                     // We calculate the largest power-of-two under the actual ratio rather than
554                     // just let the decode take care of it because we also want to remap where the
555                     // cropHint rectangle lies in the decoded [super]rect.
556                     final BitmapFactory.Options scaler;
557                     final int actualScale = cropHint.height() / wallpaper.height;
558                     int scale = 1;
559                     while (2*scale < actualScale) {
560                         scale *= 2;
561                     }
562                     if (scale > 1) {
563                         scaler = new BitmapFactory.Options();
564                         scaler.inSampleSize = scale;
565                         if (DEBUG) {
566                             Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
567                         }
568                     } else {
569                         scaler = null;
570                     }
571                     Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
572                     decoder.recycle();
573
574                     if (cropped == null) {
575                         Slog.e(TAG, "Could not decode new wallpaper");
576                     } else {
577                         // We've got the extracted crop; now we want to scale it properly to
578                         // the desired rectangle.  That's a height-biased operation: make it
579                         // fit the hinted height, and accept whatever width we end up with.
580                         cropHint.offsetTo(0, 0);
581                         cropHint.right /= scale;    // adjust by downsampling factor
582                         cropHint.bottom /= scale;
583                         final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
584                         if (DEBUG) {
585                             Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
586                         }
587                         final int destWidth = (int)(cropHint.width() * heightR);
588                         final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
589                                 destWidth, wallpaper.height, true);
590                         if (DEBUG) {
591                             Slog.v(TAG, "Final extract:");
592                             Slog.v(TAG, "  dims: w=" + wallpaper.width
593                                     + " h=" + wallpaper.height);
594                             Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
595                                     + " h=" + finalCrop.getHeight());
596                         }
597
598                         f = new FileOutputStream(wallpaper.cropFile);
599                         bos = new BufferedOutputStream(f, 32*1024);
600                         finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
601                         bos.flush();  // don't rely on the implicit flush-at-close when noting success
602                         success = true;
603                     }
604                 } catch (Exception e) {
605                     if (DEBUG) {
606                         Slog.e(TAG, "Error decoding crop", e);
607                     }
608                 } finally {
609                     IoUtils.closeQuietly(bos);
610                     IoUtils.closeQuietly(f);
611                 }
612             }
613         }
614
615         if (!success) {
616             Slog.e(TAG, "Unable to apply new wallpaper");
617             wallpaper.cropFile.delete();
618         }
619
620         if (wallpaper.cropFile.exists()) {
621             boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
622             if (DEBUG) {
623                 Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
624             }
625         }
626     }
627
628     final Context mContext;
629     final IWindowManager mIWindowManager;
630     final IPackageManager mIPackageManager;
631     final MyPackageMonitor mMonitor;
632     final AppOpsManager mAppOpsManager;
633     /**
634      * Map of color listeners per user id.
635      * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
636      */
637     final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
638     WallpaperData mLastWallpaper;
639     IWallpaperManagerCallback mKeyguardListener;
640     boolean mWaitingForUnlock;
641     boolean mShuttingDown;
642
643     /**
644      * ID of the current wallpaper, changed every time anything sets a wallpaper.
645      * This is used for external detection of wallpaper update activity.
646      */
647     int mWallpaperId;
648
649     /**
650      * Name of the component used to display bitmap wallpapers from either the gallery or
651      * built-in wallpapers.
652      */
653     final ComponentName mImageWallpaper;
654
655     /**
656      * Name of the default wallpaper component; might be different from mImageWallpaper
657      */
658     final ComponentName mDefaultWallpaperComponent;
659
660     final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
661     final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
662
663     final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
664     int mCurrentUserId;
665
666     static class WallpaperData {
667
668         int userId;
669
670         final File wallpaperFile;   // source image
671         final File cropFile;        // eventual destination
672
673         /**
674          * True while the client is writing a new wallpaper
675          */
676         boolean imageWallpaperPending;
677
678         /**
679          * Which new wallpapers are being written; mirrors the 'which'
680          * selector bit field to setWallpaper().
681          */
682         int whichPending;
683
684         /**
685          * Callback once the set + crop is finished
686          */
687         IWallpaperManagerCallback setComplete;
688
689         /**
690          * Is the OS allowed to back up this wallpaper imagery?
691          */
692         boolean allowBackup;
693
694         /**
695          * Resource name if using a picture from the wallpaper gallery
696          */
697         String name = "";
698
699         /**
700          * The component name of the currently set live wallpaper.
701          */
702         ComponentName wallpaperComponent;
703
704         /**
705          * The component name of the wallpaper that should be set next.
706          */
707         ComponentName nextWallpaperComponent;
708
709         /**
710          * The ID of this wallpaper
711          */
712         int wallpaperId;
713
714         /**
715          * Primary colors histogram
716          */
717         WallpaperColors primaryColors;
718
719         WallpaperConnection connection;
720         long lastDiedTime;
721         boolean wallpaperUpdating;
722         WallpaperObserver wallpaperObserver;
723
724         /**
725          * List of callbacks registered they should each be notified when the wallpaper is changed.
726          */
727         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
728                 = new RemoteCallbackList<IWallpaperManagerCallback>();
729
730         int width = -1;
731         int height = -1;
732
733         /**
734          * The crop hint supplied for displaying a subset of the source image
735          */
736         final Rect cropHint = new Rect(0, 0, 0, 0);
737
738         final Rect padding = new Rect(0, 0, 0, 0);
739
740         WallpaperData(int userId, String inputFileName, String cropFileName) {
741             this.userId = userId;
742             final File wallpaperDir = getWallpaperDir(userId);
743             wallpaperFile = new File(wallpaperDir, inputFileName);
744             cropFile = new File(wallpaperDir, cropFileName);
745         }
746
747         // Called during initialization of a given user's wallpaper bookkeeping
748         boolean cropExists() {
749             return cropFile.exists();
750         }
751
752         boolean sourceExists() {
753             return wallpaperFile.exists();
754         }
755     }
756
757     int makeWallpaperIdLocked() {
758         do {
759             ++mWallpaperId;
760         } while (mWallpaperId == 0);
761         return mWallpaperId;
762     }
763
764     class WallpaperConnection extends IWallpaperConnection.Stub
765             implements ServiceConnection {
766
767         /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
768          *  middle of an update). If exceeded, the wallpaper gets reset to the system default. */
769         private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
770
771         final WallpaperInfo mInfo;
772         final Binder mToken = new Binder();
773         IWallpaperService mService;
774         IWallpaperEngine mEngine;
775         WallpaperData mWallpaper;
776         IRemoteCallback mReply;
777
778         boolean mDimensionsChanged = false;
779         boolean mPaddingChanged = false;
780
781         private Runnable mResetRunnable = () -> {
782             synchronized (mLock) {
783                 if (mShuttingDown) {
784                     // Don't expect wallpaper services to relaunch during shutdown
785                     if (DEBUG_LIVE) {
786                         Slog.i(TAG, "Ignoring relaunch timeout during shutdown");
787                     }
788                     return;
789                 }
790
791                 if (!mWallpaper.wallpaperUpdating
792                         && mWallpaper.userId == mCurrentUserId) {
793                     Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
794                             + ", reverting to built-in wallpaper!");
795                     clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
796                             null);
797                 }
798             }
799         };
800
801         public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
802             mInfo = info;
803             mWallpaper = wallpaper;
804         }
805
806         @Override
807         public void onServiceConnected(ComponentName name, IBinder service) {
808             synchronized (mLock) {
809                 if (mWallpaper.connection == this) {
810                     mService = IWallpaperService.Stub.asInterface(service);
811                     attachServiceLocked(this, mWallpaper);
812                     // XXX should probably do saveSettingsLocked() later
813                     // when we have an engine, but I'm not sure about
814                     // locking there and anyway we always need to be able to
815                     // recover if there is something wrong.
816                     saveSettingsLocked(mWallpaper.userId);
817                     FgThread.getHandler().removeCallbacks(mResetRunnable);
818                 }
819             }
820         }
821
822         @Override
823         public void onServiceDisconnected(ComponentName name) {
824             synchronized (mLock) {
825                 Slog.w(TAG, "Wallpaper service gone: " + name);
826                 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
827                     Slog.e(TAG, "Does not match expected wallpaper component "
828                             + mWallpaper.wallpaperComponent);
829                 }
830                 mService = null;
831                 mEngine = null;
832                 if (mWallpaper.connection == this) {
833                     // There is an inherent ordering race between this callback and the
834                     // package monitor that receives notice that a package is being updated,
835                     // so we cannot quite trust at this moment that we know for sure that
836                     // this is not an update.  If we think this is a genuine non-update
837                     // wallpaper outage, we do our "wait for reset" work as a continuation,
838                     // a short time in the future, specifically to allow any pending package
839                     // update message on this same looper thread to be processed.
840                     if (!mWallpaper.wallpaperUpdating) {
841                         mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
842                                 1000);
843                     }
844                 }
845             }
846         }
847
848         private void processDisconnect(final ServiceConnection connection) {
849             synchronized (mLock) {
850                 // The wallpaper disappeared.  If this isn't a system-default one, track
851                 // crashes and fall back to default if it continues to misbehave.
852                 if (connection == mWallpaper.connection) {
853                     final ComponentName wpService = mWallpaper.wallpaperComponent;
854                     if (!mWallpaper.wallpaperUpdating
855                             && mWallpaper.userId == mCurrentUserId
856                             && !Objects.equals(mDefaultWallpaperComponent, wpService)
857                             && !Objects.equals(mImageWallpaper, wpService)) {
858                         // There is a race condition which causes
859                         // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
860                         // currently updating since the broadcast notifying us is async.
861                         // This race is overcome by the general rule that we only reset the
862                         // wallpaper if its service was shut down twice
863                         // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
864                         if (mWallpaper.lastDiedTime != 0
865                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
866                                 > SystemClock.uptimeMillis()) {
867                             Slog.w(TAG, "Reverting to built-in wallpaper!");
868                             clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
869                         } else {
870                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
871
872                             // If we didn't reset it right away, do so after we couldn't connect to
873                             // it for an extended amount of time to avoid having a black wallpaper.
874                             final Handler fgHandler = FgThread.getHandler();
875                             fgHandler.removeCallbacks(mResetRunnable);
876                             fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
877                             if (DEBUG_LIVE) {
878                                 Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
879                             }
880                         }
881                         final String flattened = wpService.flattenToString();
882                         EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
883                                 flattened.substring(0, Math.min(flattened.length(),
884                                         MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
885                     }
886                 } else {
887                     if (DEBUG_LIVE) {
888                         Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
889                     }
890                 }
891             }
892         }
893
894         /**
895          * Called by a live wallpaper if its colors have changed.
896          * @param primaryColors representation of wallpaper primary colors
897          */
898         @Override
899         public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
900             int which;
901             synchronized (mLock) {
902                 // Do not broadcast changes on ImageWallpaper since it's handled
903                 // internally by this class.
904                 if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
905                     return;
906                 }
907
908                 mWallpaper.primaryColors = primaryColors;
909
910                 // Live wallpapers always are system wallpapers.
911                 which = FLAG_SYSTEM;
912                 // It's also the lock screen wallpaper when we don't have a bitmap in there
913                 WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
914                 if (lockedWallpaper == null) {
915                     which |= FLAG_LOCK;
916                 }
917             }
918             if (which != 0) {
919                 notifyWallpaperColorsChanged(mWallpaper, which);
920             }
921         }
922
923         @Override
924         public void attachEngine(IWallpaperEngine engine) {
925             synchronized (mLock) {
926                 mEngine = engine;
927                 if (mDimensionsChanged) {
928                     try {
929                         mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
930                     } catch (RemoteException e) {
931                         Slog.w(TAG, "Failed to set wallpaper dimensions", e);
932                     }
933                     mDimensionsChanged = false;
934                 }
935                 if (mPaddingChanged) {
936                     try {
937                         mEngine.setDisplayPadding(mWallpaper.padding);
938                     } catch (RemoteException e) {
939                         Slog.w(TAG, "Failed to set wallpaper padding", e);
940                     }
941                     mPaddingChanged = false;
942                 }
943                 try {
944                     // This will trigger onComputeColors in the wallpaper engine.
945                     // It's fine to be locked in here since the binder is oneway.
946                     mEngine.requestWallpaperColors();
947                 } catch (RemoteException e) {
948                     Slog.w(TAG, "Failed to request wallpaper colors", e);
949                 }
950             }
951         }
952
953         @Override
954         public void engineShown(IWallpaperEngine engine) {
955             synchronized (mLock) {
956                 if (mReply != null) {
957                     long ident = Binder.clearCallingIdentity();
958                     try {
959                         mReply.sendResult(null);
960                     } catch (RemoteException e) {
961                         Binder.restoreCallingIdentity(ident);
962                     }
963                     mReply = null;
964                 }
965             }
966         }
967
968         @Override
969         public ParcelFileDescriptor setWallpaper(String name) {
970             synchronized (mLock) {
971                 if (mWallpaper.connection == this) {
972                     return updateWallpaperBitmapLocked(name, mWallpaper, null);
973                 }
974                 return null;
975             }
976         }
977     }
978
979     class MyPackageMonitor extends PackageMonitor {
980         @Override
981         public void onPackageUpdateFinished(String packageName, int uid) {
982             synchronized (mLock) {
983                 if (mCurrentUserId != getChangingUserId()) {
984                     return;
985                 }
986                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
987                 if (wallpaper != null) {
988                     final ComponentName wpService = wallpaper.wallpaperComponent;
989                     if (wpService != null && wpService.getPackageName().equals(packageName)) {
990                         if (DEBUG_LIVE) {
991                             Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
992                         }
993                         wallpaper.wallpaperUpdating = false;
994                         clearWallpaperComponentLocked(wallpaper);
995                         if (!bindWallpaperComponentLocked(wpService, false, false,
996                                 wallpaper, null)) {
997                             Slog.w(TAG, "Wallpaper " + wpService
998                                     + " no longer available; reverting to default");
999                             clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1000                         }
1001                     }
1002                 }
1003             }
1004         }
1005
1006         @Override
1007         public void onPackageModified(String packageName) {
1008             synchronized (mLock) {
1009                 if (mCurrentUserId != getChangingUserId()) {
1010                     return;
1011                 }
1012                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1013                 if (wallpaper != null) {
1014                     if (wallpaper.wallpaperComponent == null
1015                             || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1016                         return;
1017                     }
1018                     doPackagesChangedLocked(true, wallpaper);
1019                 }
1020             }
1021         }
1022
1023         @Override
1024         public void onPackageUpdateStarted(String packageName, int uid) {
1025             synchronized (mLock) {
1026                 if (mCurrentUserId != getChangingUserId()) {
1027                     return;
1028                 }
1029                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1030                 if (wallpaper != null) {
1031                     if (wallpaper.wallpaperComponent != null
1032                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1033                         if (DEBUG_LIVE) {
1034                             Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent
1035                                     + " is updating");
1036                         }
1037                         wallpaper.wallpaperUpdating = true;
1038                         if (wallpaper.connection != null) {
1039                             FgThread.getHandler().removeCallbacks(
1040                                     wallpaper.connection.mResetRunnable);
1041                         }
1042                     }
1043                 }
1044             }
1045         }
1046
1047         @Override
1048         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1049             synchronized (mLock) {
1050                 boolean changed = false;
1051                 if (mCurrentUserId != getChangingUserId()) {
1052                     return false;
1053                 }
1054                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1055                 if (wallpaper != null) {
1056                     boolean res = doPackagesChangedLocked(doit, wallpaper);
1057                     changed |= res;
1058                 }
1059                 return changed;
1060             }
1061         }
1062
1063         @Override
1064         public void onSomePackagesChanged() {
1065             synchronized (mLock) {
1066                 if (mCurrentUserId != getChangingUserId()) {
1067                     return;
1068                 }
1069                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1070                 if (wallpaper != null) {
1071                     doPackagesChangedLocked(true, wallpaper);
1072                 }
1073             }
1074         }
1075
1076         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
1077             boolean changed = false;
1078             if (wallpaper.wallpaperComponent != null) {
1079                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
1080                         .getPackageName());
1081                 if (change == PACKAGE_PERMANENT_CHANGE
1082                         || change == PACKAGE_TEMPORARY_CHANGE) {
1083                     changed = true;
1084                     if (doit) {
1085                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
1086                                 + wallpaper.wallpaperComponent);
1087                         clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1088                     }
1089                 }
1090             }
1091             if (wallpaper.nextWallpaperComponent != null) {
1092                 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
1093                         .getPackageName());
1094                 if (change == PACKAGE_PERMANENT_CHANGE
1095                         || change == PACKAGE_TEMPORARY_CHANGE) {
1096                     wallpaper.nextWallpaperComponent = null;
1097                 }
1098             }
1099             if (wallpaper.wallpaperComponent != null
1100                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
1101                 try {
1102                     mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
1103                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1104                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1105                 } catch (NameNotFoundException e) {
1106                     Slog.w(TAG, "Wallpaper component gone, removing: "
1107                             + wallpaper.wallpaperComponent);
1108                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1109                 }
1110             }
1111             if (wallpaper.nextWallpaperComponent != null
1112                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
1113                 try {
1114                     mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
1115                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1116                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1117                 } catch (NameNotFoundException e) {
1118                     wallpaper.nextWallpaperComponent = null;
1119                 }
1120             }
1121             return changed;
1122         }
1123     }
1124
1125     public WallpaperManagerService(Context context) {
1126         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
1127         mContext = context;
1128         mShuttingDown = false;
1129         mImageWallpaper = ComponentName.unflattenFromString(
1130                 context.getResources().getString(R.string.image_wallpaper_component));
1131         mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
1132         mIWindowManager = IWindowManager.Stub.asInterface(
1133                 ServiceManager.getService(Context.WINDOW_SERVICE));
1134         mIPackageManager = AppGlobals.getPackageManager();
1135         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1136         mMonitor = new MyPackageMonitor();
1137         mMonitor.register(context, null, UserHandle.ALL, true);
1138         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
1139         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
1140         mColorsChangedListeners = new SparseArray<>();
1141     }
1142
1143     private static File getWallpaperDir(int userId) {
1144         return Environment.getUserSystemDirectory(userId);
1145     }
1146
1147     @Override
1148     protected void finalize() throws Throwable {
1149         super.finalize();
1150         for (int i = 0; i < mWallpaperMap.size(); i++) {
1151             WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1152             wallpaper.wallpaperObserver.stopWatching();
1153         }
1154     }
1155
1156     void systemReady() {
1157         if (DEBUG) Slog.v(TAG, "systemReady");
1158         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1159         // If we think we're going to be using the system image wallpaper imagery, make
1160         // sure we have something to render
1161         if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
1162             // No crop file? Make sure we've finished the processing sequence if necessary
1163             if (!wallpaper.cropExists()) {
1164                 if (DEBUG) {
1165                     Slog.i(TAG, "No crop; regenerating from source");
1166                 }
1167                 generateCrop(wallpaper);
1168             }
1169             // Still nothing?  Fall back to default.
1170             if (!wallpaper.cropExists()) {
1171                 if (DEBUG) {
1172                     Slog.i(TAG, "Unable to regenerate crop; resetting");
1173                 }
1174                 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
1175             }
1176         } else {
1177             if (DEBUG) {
1178                 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
1179             }
1180         }
1181
1182         IntentFilter userFilter = new IntentFilter();
1183         userFilter.addAction(Intent.ACTION_USER_REMOVED);
1184         mContext.registerReceiver(new BroadcastReceiver() {
1185             @Override
1186             public void onReceive(Context context, Intent intent) {
1187                 final String action = intent.getAction();
1188                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
1189                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
1190                             UserHandle.USER_NULL));
1191                 }
1192             }
1193         }, userFilter);
1194
1195         final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
1196         mContext.registerReceiver(new BroadcastReceiver() {
1197             @Override
1198             public void onReceive(Context context, Intent intent) {
1199                 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
1200                     if (DEBUG) {
1201                         Slog.i(TAG, "Shutting down");
1202                     }
1203                     synchronized (mLock) {
1204                         mShuttingDown = true;
1205                     }
1206                 }
1207             }
1208         }, shutdownFilter);
1209
1210         try {
1211             ActivityManager.getService().registerUserSwitchObserver(
1212                     new UserSwitchObserver() {
1213                         @Override
1214                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
1215                             switchUser(newUserId, reply);
1216                         }
1217                     }, TAG);
1218         } catch (RemoteException e) {
1219             e.rethrowAsRuntimeException();
1220         }
1221     }
1222
1223     /** Called by SystemBackupAgent */
1224     public String getName() {
1225         // Verify caller is the system
1226         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1227             throw new RuntimeException("getName() can only be called from the system process");
1228         }
1229         synchronized (mLock) {
1230             return mWallpaperMap.get(0).name;
1231         }
1232     }
1233
1234     void stopObserver(WallpaperData wallpaper) {
1235         if (wallpaper != null) {
1236             if (wallpaper.wallpaperObserver != null) {
1237                 wallpaper.wallpaperObserver.stopWatching();
1238                 wallpaper.wallpaperObserver = null;
1239             }
1240         }
1241     }
1242
1243     void stopObserversLocked(int userId) {
1244         stopObserver(mWallpaperMap.get(userId));
1245         stopObserver(mLockWallpaperMap.get(userId));
1246         mWallpaperMap.remove(userId);
1247         mLockWallpaperMap.remove(userId);
1248     }
1249
1250     void onUnlockUser(final int userId) {
1251         synchronized (mLock) {
1252             if (mCurrentUserId == userId) {
1253                 if (mWaitingForUnlock) {
1254                     // If we're switching users, now is when we transition the wallpaper
1255                     switchUser(userId, null);
1256                 }
1257
1258                 // Make sure that the SELinux labeling of all the relevant files is correct.
1259                 // This corrects for mislabeling bugs that might have arisen from move-to
1260                 // operations involving the wallpaper files.  This isn't timing-critical,
1261                 // so we do it in the background to avoid holding up the user unlock operation.
1262                 if (mUserRestorecon.get(userId) != Boolean.TRUE) {
1263                     mUserRestorecon.put(userId, Boolean.TRUE);
1264                     Runnable relabeler = new Runnable() {
1265                         @Override
1266                         public void run() {
1267                             final File wallpaperDir = getWallpaperDir(userId);
1268                             for (String filename : sPerUserFiles) {
1269                                 File f = new File(wallpaperDir, filename);
1270                                 if (f.exists()) {
1271                                     SELinux.restorecon(f);
1272                                 }
1273                             }
1274                         }
1275                     };
1276                     BackgroundThread.getHandler().post(relabeler);
1277                 }
1278             }
1279         }
1280     }
1281
1282     void onRemoveUser(int userId) {
1283         if (userId < 1) return;
1284
1285         final File wallpaperDir = getWallpaperDir(userId);
1286         synchronized (mLock) {
1287             stopObserversLocked(userId);
1288             for (String filename : sPerUserFiles) {
1289                 new File(wallpaperDir, filename).delete();
1290             }
1291             mUserRestorecon.remove(userId);
1292         }
1293     }
1294
1295     void switchUser(int userId, IRemoteCallback reply) {
1296         WallpaperData systemWallpaper;
1297         WallpaperData lockWallpaper;
1298         synchronized (mLock) {
1299             mCurrentUserId = userId;
1300             systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1301             lockWallpaper = mLockWallpaperMap.get(userId);
1302             if (lockWallpaper == null) {
1303                 lockWallpaper = systemWallpaper;
1304             }
1305             // Not started watching yet, in case wallpaper data was loaded for other reasons.
1306             if (systemWallpaper.wallpaperObserver == null) {
1307                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
1308                 systemWallpaper.wallpaperObserver.startWatching();
1309             }
1310             switchWallpaper(systemWallpaper, reply);
1311         }
1312         notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
1313         notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
1314     }
1315
1316     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
1317         synchronized (mLock) {
1318             mWaitingForUnlock = false;
1319             final ComponentName cname = wallpaper.wallpaperComponent != null ?
1320                     wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
1321             if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
1322                 // We failed to bind the desired wallpaper, but that might
1323                 // happen if the wallpaper isn't direct-boot aware
1324                 ServiceInfo si = null;
1325                 try {
1326                     si = mIPackageManager.getServiceInfo(cname,
1327                             PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
1328                 } catch (RemoteException ignored) {
1329                 }
1330
1331                 if (si == null) {
1332                     Slog.w(TAG, "Failure starting previous wallpaper; clearing");
1333                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
1334                 } else {
1335                     Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
1336                     // We might end up persisting the current wallpaper data
1337                     // while locked, so pretend like the component was actually
1338                     // bound into place
1339                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
1340                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
1341                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1342                     ensureSaneWallpaperData(fallback);
1343                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
1344                     mWaitingForUnlock = true;
1345                 }
1346             }
1347         }
1348     }
1349
1350     @Override
1351     public void clearWallpaper(String callingPackage, int which, int userId) {
1352         if (DEBUG) Slog.v(TAG, "clearWallpaper");
1353         checkPermission(android.Manifest.permission.SET_WALLPAPER);
1354         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1355             return;
1356         }
1357         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1358                 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
1359
1360         WallpaperData data = null;
1361         synchronized (mLock) {
1362             clearWallpaperLocked(false, which, userId, null);
1363
1364             if (which == FLAG_LOCK) {
1365                 data = mLockWallpaperMap.get(userId);
1366             }
1367             if (which == FLAG_SYSTEM || data == null) {
1368                 data = mWallpaperMap.get(userId);
1369             }
1370         }
1371
1372         // When clearing a wallpaper, broadcast new valid colors
1373         if (data != null) {
1374             notifyWallpaperColorsChanged(data, which);
1375         }
1376     }
1377
1378     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
1379         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1380             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
1381         }
1382
1383         WallpaperData wallpaper = null;
1384         if (which == FLAG_LOCK) {
1385             wallpaper = mLockWallpaperMap.get(userId);
1386             if (wallpaper == null) {
1387                 // It's already gone; we're done.
1388                 if (DEBUG) {
1389                     Slog.i(TAG, "Lock wallpaper already cleared");
1390                 }
1391                 return;
1392             }
1393         } else {
1394             wallpaper = mWallpaperMap.get(userId);
1395             if (wallpaper == null) {
1396                 // Might need to bring it in the first time to establish our rewrite
1397                 loadSettingsLocked(userId, false);
1398                 wallpaper = mWallpaperMap.get(userId);
1399             }
1400         }
1401         if (wallpaper == null) {
1402             return;
1403         }
1404
1405         final long ident = Binder.clearCallingIdentity();
1406         try {
1407             if (wallpaper.wallpaperFile.exists()) {
1408                 wallpaper.wallpaperFile.delete();
1409                 wallpaper.cropFile.delete();
1410                 if (which == FLAG_LOCK) {
1411                     mLockWallpaperMap.remove(userId);
1412                     final IWallpaperManagerCallback cb = mKeyguardListener;
1413                     if (cb != null) {
1414                         if (DEBUG) {
1415                             Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
1416                         }
1417                         try {
1418                             cb.onWallpaperChanged();
1419                         } catch (RemoteException e) {
1420                             // Oh well it went away; no big deal
1421                         }
1422                     }
1423                     saveSettingsLocked(userId);
1424                     return;
1425                 }
1426             }
1427
1428             RuntimeException e = null;
1429             try {
1430                 wallpaper.primaryColors = null;
1431                 wallpaper.imageWallpaperPending = false;
1432                 if (userId != mCurrentUserId) return;
1433                 if (bindWallpaperComponentLocked(defaultFailed
1434                         ? mImageWallpaper
1435                                 : null, true, false, wallpaper, reply)) {
1436                     return;
1437                 }
1438             } catch (IllegalArgumentException e1) {
1439                 e = e1;
1440             }
1441
1442             // This can happen if the default wallpaper component doesn't
1443             // exist.  This should be a system configuration problem, but
1444             // let's not let it crash the system and just live with no
1445             // wallpaper.
1446             Slog.e(TAG, "Default wallpaper component not found!", e);
1447             clearWallpaperComponentLocked(wallpaper);
1448             if (reply != null) {
1449                 try {
1450                     reply.sendResult(null);
1451                 } catch (RemoteException e1) {
1452                 }
1453             }
1454         } finally {
1455             Binder.restoreCallingIdentity(ident);
1456         }
1457     }
1458
1459     public boolean hasNamedWallpaper(String name) {
1460         synchronized (mLock) {
1461             List<UserInfo> users;
1462             long ident = Binder.clearCallingIdentity();
1463             try {
1464                 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
1465             } finally {
1466                 Binder.restoreCallingIdentity(ident);
1467             }
1468             for (UserInfo user: users) {
1469                 // ignore managed profiles
1470                 if (user.isManagedProfile()) {
1471                     continue;
1472                 }
1473                 WallpaperData wd = mWallpaperMap.get(user.id);
1474                 if (wd == null) {
1475                     // User hasn't started yet, so load her settings to peek at the wallpaper
1476                     loadSettingsLocked(user.id, false);
1477                     wd = mWallpaperMap.get(user.id);
1478                 }
1479                 if (wd != null && name.equals(wd.name)) {
1480                     return true;
1481                 }
1482             }
1483         }
1484         return false;
1485     }
1486
1487     private Point getDefaultDisplaySize() {
1488         Point p = new Point();
1489         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
1490         Display d = wm.getDefaultDisplay();
1491         d.getRealSize(p);
1492         return p;
1493     }
1494
1495     public void setDimensionHints(int width, int height, String callingPackage)
1496             throws RemoteException {
1497         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1498         if (!isWallpaperSupported(callingPackage)) {
1499             return;
1500         }
1501         synchronized (mLock) {
1502             int userId = UserHandle.getCallingUserId();
1503             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1504             if (width <= 0 || height <= 0) {
1505                 throw new IllegalArgumentException("width and height must be > 0");
1506             }
1507             // Make sure it is at least as large as the display.
1508             Point displaySize = getDefaultDisplaySize();
1509             width = Math.max(width, displaySize.x);
1510             height = Math.max(height, displaySize.y);
1511
1512             if (width != wallpaper.width || height != wallpaper.height) {
1513                 wallpaper.width = width;
1514                 wallpaper.height = height;
1515                 saveSettingsLocked(userId);
1516                 if (mCurrentUserId != userId) return; // Don't change the properties now
1517                 if (wallpaper.connection != null) {
1518                     if (wallpaper.connection.mEngine != null) {
1519                         try {
1520                             wallpaper.connection.mEngine.setDesiredSize(
1521                                     width, height);
1522                         } catch (RemoteException e) {
1523                         }
1524                         notifyCallbacksLocked(wallpaper);
1525                     } else if (wallpaper.connection.mService != null) {
1526                         // We've attached to the service but the engine hasn't attached back to us
1527                         // yet. This means it will be created with the previous dimensions, so we
1528                         // need to update it to the new dimensions once it attaches.
1529                         wallpaper.connection.mDimensionsChanged = true;
1530                     }
1531                 }
1532             }
1533         }
1534     }
1535
1536     public int getWidthHint() throws RemoteException {
1537         synchronized (mLock) {
1538             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1539             if (wallpaper != null) {
1540                 return wallpaper.width;
1541             } else {
1542                 return 0;
1543             }
1544         }
1545     }
1546
1547     public int getHeightHint() throws RemoteException {
1548         synchronized (mLock) {
1549             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1550             if (wallpaper != null) {
1551                 return wallpaper.height;
1552             } else {
1553                 return 0;
1554             }
1555         }
1556     }
1557
1558     public void setDisplayPadding(Rect padding, String callingPackage) {
1559         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1560         if (!isWallpaperSupported(callingPackage)) {
1561             return;
1562         }
1563         synchronized (mLock) {
1564             int userId = UserHandle.getCallingUserId();
1565             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1566             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
1567                 throw new IllegalArgumentException("padding must be positive: " + padding);
1568             }
1569
1570             if (!padding.equals(wallpaper.padding)) {
1571                 wallpaper.padding.set(padding);
1572                 saveSettingsLocked(userId);
1573                 if (mCurrentUserId != userId) return; // Don't change the properties now
1574                 if (wallpaper.connection != null) {
1575                     if (wallpaper.connection.mEngine != null) {
1576                         try {
1577                             wallpaper.connection.mEngine.setDisplayPadding(padding);
1578                         } catch (RemoteException e) {
1579                         }
1580                         notifyCallbacksLocked(wallpaper);
1581                     } else if (wallpaper.connection.mService != null) {
1582                         // We've attached to the service but the engine hasn't attached back to us
1583                         // yet. This means it will be created with the previous dimensions, so we
1584                         // need to update it to the new dimensions once it attaches.
1585                         wallpaper.connection.mPaddingChanged = true;
1586                     }
1587                 }
1588             }
1589         }
1590     }
1591
1592     private void enforceCallingOrSelfPermissionAndAppOp(String permission, final String callingPkg,
1593             final int callingUid, String message) {
1594         mContext.enforceCallingOrSelfPermission(permission, message);
1595
1596         final String opName = AppOpsManager.permissionToOp(permission);
1597         if (opName != null) {
1598             final int appOpMode = mAppOpsManager.noteOp(opName, callingUid, callingPkg);
1599             if (appOpMode != AppOpsManager.MODE_ALLOWED) {
1600                 throw new SecurityException(
1601                         message + ": " + callingPkg + " is not allowed to " + permission);
1602             }
1603         }
1604     }
1605
1606     @Override
1607     public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
1608             final int which, Bundle outParams, int wallpaperUserId) {
1609         final int hasPrivilege = mContext.checkCallingOrSelfPermission(
1610                 android.Manifest.permission.READ_WALLPAPER_INTERNAL);
1611         if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
1612             enforceCallingOrSelfPermissionAndAppOp(android.Manifest.permission.READ_EXTERNAL_STORAGE,
1613                     callingPkg, Binder.getCallingUid(), "read wallpaper");
1614         }
1615
1616         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1617                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
1618
1619         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1620             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
1621         }
1622
1623         synchronized (mLock) {
1624             final SparseArray<WallpaperData> whichSet =
1625                     (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1626             WallpaperData wallpaper = whichSet.get(wallpaperUserId);
1627             if (wallpaper == null) {
1628                 // common case, this is the first lookup post-boot of the system or
1629                 // unified lock, so we bring up the saved state lazily now and recheck.
1630                 loadSettingsLocked(wallpaperUserId, false);
1631                 wallpaper = whichSet.get(wallpaperUserId);
1632                 if (wallpaper == null) {
1633                     return null;
1634                 }
1635             }
1636             try {
1637                 if (outParams != null) {
1638                     outParams.putInt("width", wallpaper.width);
1639                     outParams.putInt("height", wallpaper.height);
1640                 }
1641                 if (cb != null) {
1642                     wallpaper.callbacks.register(cb);
1643                 }
1644                 if (!wallpaper.cropFile.exists()) {
1645                     return null;
1646                 }
1647                 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
1648             } catch (FileNotFoundException e) {
1649                 /* Shouldn't happen as we check to see if the file exists */
1650                 Slog.w(TAG, "Error getting wallpaper", e);
1651             }
1652             return null;
1653         }
1654     }
1655
1656     @Override
1657     public WallpaperInfo getWallpaperInfo(int userId) {
1658         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1659                 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
1660         synchronized (mLock) {
1661             WallpaperData wallpaper = mWallpaperMap.get(userId);
1662             if (wallpaper != null && wallpaper.connection != null) {
1663                 return wallpaper.connection.mInfo;
1664             }
1665             return null;
1666         }
1667     }
1668
1669     @Override
1670     public int getWallpaperIdForUser(int which, int userId) {
1671         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1672                 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
1673
1674         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1675             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
1676         }
1677
1678         final SparseArray<WallpaperData> map =
1679                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1680         synchronized (mLock) {
1681             WallpaperData wallpaper = map.get(userId);
1682             if (wallpaper != null) {
1683                 return wallpaper.wallpaperId;
1684             }
1685         }
1686         return -1;
1687     }
1688
1689     @Override
1690     public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
1691         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1692                 userId, true, true, "registerWallpaperColorsCallback", null);
1693         synchronized (mLock) {
1694             RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
1695                     mColorsChangedListeners.get(userId);
1696             if (userColorsChangedListeners == null) {
1697                 userColorsChangedListeners = new RemoteCallbackList<>();
1698                 mColorsChangedListeners.put(userId, userColorsChangedListeners);
1699             }
1700             userColorsChangedListeners.register(cb);
1701         }
1702     }
1703
1704     @Override
1705     public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
1706         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1707                 userId, true, true, "unregisterWallpaperColorsCallback", null);
1708         synchronized (mLock) {
1709             final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
1710                     mColorsChangedListeners.get(userId);
1711             if (userColorsChangedListeners != null) {
1712                 userColorsChangedListeners.unregister(cb);
1713             }
1714         }
1715     }
1716
1717     @Override
1718     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
1719         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
1720         synchronized (mLock) {
1721             mKeyguardListener = cb;
1722         }
1723         return true;
1724     }
1725
1726     @Override
1727     public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
1728         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
1729             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
1730         }
1731         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1732                 userId, false, true, "getWallpaperColors", null);
1733
1734         WallpaperData wallpaperData = null;
1735         boolean shouldExtract;
1736
1737         synchronized (mLock) {
1738             if (which == FLAG_LOCK) {
1739                 wallpaperData = mLockWallpaperMap.get(userId);
1740             }
1741
1742             // Try to get the system wallpaper anyway since it might
1743             // also be the lock screen wallpaper
1744             if (wallpaperData == null) {
1745                 wallpaperData = mWallpaperMap.get(userId);
1746             }
1747
1748             if (wallpaperData == null) {
1749                 return null;
1750             }
1751             shouldExtract = wallpaperData.primaryColors == null;
1752         }
1753
1754         if (shouldExtract) {
1755             extractColors(wallpaperData);
1756         }
1757
1758         synchronized (mLock) {
1759             return wallpaperData.primaryColors;
1760         }
1761     }
1762
1763     @Override
1764     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
1765             Rect cropHint, boolean allowBackup, Bundle extras, int which,
1766             IWallpaperManagerCallback completion, int userId) {
1767         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
1768                 false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
1769         checkPermission(android.Manifest.permission.SET_WALLPAPER);
1770
1771         if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
1772             final String msg = "Must specify a valid wallpaper category to set";
1773             Slog.e(TAG, msg);
1774             throw new IllegalArgumentException(msg);
1775         }
1776
1777         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1778             return null;
1779         }
1780
1781         // "null" means the no-op crop, preserving the full input image
1782         if (cropHint == null) {
1783             cropHint = new Rect(0, 0, 0, 0);
1784         } else {
1785             if (cropHint.isEmpty()
1786                     || cropHint.left < 0
1787                     || cropHint.top < 0) {
1788                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
1789             }
1790         }
1791
1792         synchronized (mLock) {
1793             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
1794             WallpaperData wallpaper;
1795
1796             /* If we're setting system but not lock, and lock is currently sharing the system
1797              * wallpaper, we need to migrate that image over to being lock-only before
1798              * the caller here writes new bitmap data.
1799              */
1800             if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
1801                 if (DEBUG) {
1802                     Slog.i(TAG, "Migrating system->lock to preserve");
1803                 }
1804                 migrateSystemToLockWallpaperLocked(userId);
1805             }
1806
1807             wallpaper = getWallpaperSafeLocked(userId, which);
1808             final long ident = Binder.clearCallingIdentity();
1809             try {
1810                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
1811                 if (pfd != null) {
1812                     wallpaper.imageWallpaperPending = true;
1813                     wallpaper.whichPending = which;
1814                     wallpaper.setComplete = completion;
1815                     wallpaper.cropHint.set(cropHint);
1816                     wallpaper.allowBackup = allowBackup;
1817                 }
1818                 return pfd;
1819             } finally {
1820                 Binder.restoreCallingIdentity(ident);
1821             }
1822         }
1823     }
1824
1825     private void migrateSystemToLockWallpaperLocked(int userId) {
1826         WallpaperData sysWP = mWallpaperMap.get(userId);
1827         if (sysWP == null) {
1828             if (DEBUG) {
1829                 Slog.i(TAG, "No system wallpaper?  Not tracking for lock-only");
1830             }
1831             return;
1832         }
1833
1834         // We know a-priori that there is no lock-only wallpaper currently
1835         WallpaperData lockWP = new WallpaperData(userId,
1836                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1837         lockWP.wallpaperId = sysWP.wallpaperId;
1838         lockWP.cropHint.set(sysWP.cropHint);
1839         lockWP.width = sysWP.width;
1840         lockWP.height = sysWP.height;
1841         lockWP.allowBackup = sysWP.allowBackup;
1842         lockWP.primaryColors = sysWP.primaryColors;
1843
1844         // Migrate the bitmap files outright; no need to copy
1845         try {
1846             Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
1847             Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
1848         } catch (ErrnoException e) {
1849             Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
1850             lockWP.wallpaperFile.delete();
1851             lockWP.cropFile.delete();
1852             return;
1853         }
1854
1855         mLockWallpaperMap.put(userId, lockWP);
1856     }
1857
1858     ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
1859             Bundle extras) {
1860         if (name == null) name = "";
1861         try {
1862             File dir = getWallpaperDir(wallpaper.userId);
1863             if (!dir.exists()) {
1864                 dir.mkdir();
1865                 FileUtils.setPermissions(
1866                         dir.getPath(),
1867                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
1868                         -1, -1);
1869             }
1870             ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
1871                     MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
1872             if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
1873                 return null;
1874             }
1875             wallpaper.name = name;
1876             wallpaper.wallpaperId = makeWallpaperIdLocked();
1877             if (extras != null) {
1878                 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
1879             }
1880             // Nullify field to require new computation
1881             wallpaper.primaryColors = null;
1882             if (DEBUG) {
1883                 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
1884                         + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
1885             }
1886             return fd;
1887         } catch (FileNotFoundException e) {
1888             Slog.w(TAG, "Error setting wallpaper", e);
1889         }
1890         return null;
1891     }
1892
1893     @Override
1894     public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
1895             int userId) {
1896
1897         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
1898             setWallpaperComponent(name, userId);
1899         }
1900     }
1901
1902     // ToDo: Remove this version of the function
1903     @Override
1904     public void setWallpaperComponent(ComponentName name) {
1905         setWallpaperComponent(name, UserHandle.getCallingUserId());
1906     }
1907
1908     private void setWallpaperComponent(ComponentName name, int userId) {
1909         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
1910                 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
1911         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
1912
1913         int which = FLAG_SYSTEM;
1914         boolean shouldNotifyColors = false;
1915         WallpaperData wallpaper;
1916
1917         synchronized (mLock) {
1918             if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
1919             wallpaper = mWallpaperMap.get(userId);
1920             if (wallpaper == null) {
1921                 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
1922             }
1923             final long ident = Binder.clearCallingIdentity();
1924
1925             // Live wallpapers can't be specified for keyguard.  If we're using a static
1926             // system+lock image currently, migrate the system wallpaper to be a lock-only
1927             // image as part of making a different live component active as the system
1928             // wallpaper.
1929             if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
1930                 if (mLockWallpaperMap.get(userId) == null) {
1931                     // We're using the static imagery and there is no lock-specific image in place,
1932                     // therefore it's a shared system+lock image that we need to migrate.
1933                     migrateSystemToLockWallpaperLocked(userId);
1934                 }
1935             }
1936
1937             // New live wallpaper is also a lock wallpaper if nothing is set
1938             if (mLockWallpaperMap.get(userId) == null) {
1939                 which |= FLAG_LOCK;
1940             }
1941
1942             try {
1943                 wallpaper.imageWallpaperPending = false;
1944                 boolean same = changingToSame(name, wallpaper);
1945                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
1946                     if (!same) {
1947                         wallpaper.primaryColors = null;
1948                     }
1949                     wallpaper.wallpaperId = makeWallpaperIdLocked();
1950                     notifyCallbacksLocked(wallpaper);
1951                     shouldNotifyColors = true;
1952                 }
1953             } finally {
1954                 Binder.restoreCallingIdentity(ident);
1955             }
1956         }
1957
1958         if (shouldNotifyColors) {
1959             notifyWallpaperColorsChanged(wallpaper, which);
1960         }
1961     }
1962
1963     private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
1964         if (wallpaper.connection != null) {
1965             if (wallpaper.wallpaperComponent == null) {
1966                 if (componentName == null) {
1967                     if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
1968                     // Still using default wallpaper.
1969                     return true;
1970                 }
1971             } else if (wallpaper.wallpaperComponent.equals(componentName)) {
1972                 // Changing to same wallpaper.
1973                 if (DEBUG) Slog.v(TAG, "same wallpaper");
1974                 return true;
1975             }
1976         }
1977         return false;
1978     }
1979
1980     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
1981             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
1982         if (DEBUG_LIVE) {
1983             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
1984         }
1985         // Has the component changed?
1986         if (!force && changingToSame(componentName, wallpaper)) {
1987             return true;
1988         }
1989
1990         try {
1991             if (componentName == null) {
1992                 componentName = mDefaultWallpaperComponent;
1993                 if (componentName == null) {
1994                     // Fall back to static image wallpaper
1995                     componentName = mImageWallpaper;
1996                     //clearWallpaperComponentLocked();
1997                     //return;
1998                     if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
1999                 }
2000             }
2001             int serviceUserId = wallpaper.userId;
2002             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
2003                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
2004             if (si == null) {
2005                 // The wallpaper component we're trying to use doesn't exist
2006                 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
2007                 return false;
2008             }
2009             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
2010                 String msg = "Selected service does not require "
2011                         + android.Manifest.permission.BIND_WALLPAPER
2012                         + ": " + componentName;
2013                 if (fromUser) {
2014                     throw new SecurityException(msg);
2015                 }
2016                 Slog.w(TAG, msg);
2017                 return false;
2018             }
2019
2020             WallpaperInfo wi = null;
2021
2022             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
2023             if (componentName != null && !componentName.equals(mImageWallpaper)) {
2024                 // Make sure the selected service is actually a wallpaper service.
2025                 List<ResolveInfo> ris =
2026                         mIPackageManager.queryIntentServices(intent,
2027                                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2028                                 PackageManager.GET_META_DATA, serviceUserId).getList();
2029                 for (int i=0; i<ris.size(); i++) {
2030                     ServiceInfo rsi = ris.get(i).serviceInfo;
2031                     if (rsi.name.equals(si.name) &&
2032                             rsi.packageName.equals(si.packageName)) {
2033                         try {
2034                             wi = new WallpaperInfo(mContext, ris.get(i));
2035                         } catch (XmlPullParserException e) {
2036                             if (fromUser) {
2037                                 throw new IllegalArgumentException(e);
2038                             }
2039                             Slog.w(TAG, e);
2040                             return false;
2041                         } catch (IOException e) {
2042                             if (fromUser) {
2043                                 throw new IllegalArgumentException(e);
2044                             }
2045                             Slog.w(TAG, e);
2046                             return false;
2047                         }
2048                         break;
2049                     }
2050                 }
2051                 if (wi == null) {
2052                     String msg = "Selected service is not a wallpaper: "
2053                             + componentName;
2054                     if (fromUser) {
2055                         throw new SecurityException(msg);
2056                     }
2057                     Slog.w(TAG, msg);
2058                     return false;
2059                 }
2060             }
2061
2062             // Bind the service!
2063             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
2064             WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
2065             intent.setComponent(componentName);
2066             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
2067                     com.android.internal.R.string.wallpaper_binding_label);
2068             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
2069                     mContext, 0,
2070                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
2071                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
2072                     0, null, new UserHandle(serviceUserId)));
2073             if (!mContext.bindServiceAsUser(intent, newConn,
2074                     Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
2075                             | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
2076                     new UserHandle(serviceUserId))) {
2077                 String msg = "Unable to bind service: "
2078                         + componentName;
2079                 if (fromUser) {
2080                     throw new IllegalArgumentException(msg);
2081                 }
2082                 Slog.w(TAG, msg);
2083                 return false;
2084             }
2085             if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
2086                 detachWallpaperLocked(mLastWallpaper);
2087             }
2088             wallpaper.wallpaperComponent = componentName;
2089             wallpaper.connection = newConn;
2090             newConn.mReply = reply;
2091             try {
2092                 if (wallpaper.userId == mCurrentUserId) {
2093                     if (DEBUG)
2094                         Slog.v(TAG, "Adding window token: " + newConn.mToken);
2095                     mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
2096                     mLastWallpaper = wallpaper;
2097                 }
2098             } catch (RemoteException e) {
2099             }
2100         } catch (RemoteException e) {
2101             String msg = "Remote exception for " + componentName + "\n" + e;
2102             if (fromUser) {
2103                 throw new IllegalArgumentException(msg);
2104             }
2105             Slog.w(TAG, msg);
2106             return false;
2107         }
2108         return true;
2109     }
2110
2111     void detachWallpaperLocked(WallpaperData wallpaper) {
2112         if (wallpaper.connection != null) {
2113             if (wallpaper.connection.mReply != null) {
2114                 try {
2115                     wallpaper.connection.mReply.sendResult(null);
2116                 } catch (RemoteException e) {
2117                 }
2118                 wallpaper.connection.mReply = null;
2119             }
2120             if (wallpaper.connection.mEngine != null) {
2121                 try {
2122                     wallpaper.connection.mEngine.destroy();
2123                 } catch (RemoteException e) {
2124                 }
2125             }
2126             mContext.unbindService(wallpaper.connection);
2127             try {
2128                 if (DEBUG)
2129                     Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
2130                 mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
2131             } catch (RemoteException e) {
2132             }
2133             wallpaper.connection.mService = null;
2134             wallpaper.connection.mEngine = null;
2135             wallpaper.connection = null;
2136         }
2137     }
2138
2139     void clearWallpaperComponentLocked(WallpaperData wallpaper) {
2140         wallpaper.wallpaperComponent = null;
2141         detachWallpaperLocked(wallpaper);
2142     }
2143
2144     void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
2145         try {
2146             conn.mService.attach(conn, conn.mToken,
2147                     TYPE_WALLPAPER, false,
2148                     wallpaper.width, wallpaper.height, wallpaper.padding);
2149         } catch (RemoteException e) {
2150             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
2151             if (!wallpaper.wallpaperUpdating) {
2152                 bindWallpaperComponentLocked(null, false, false, wallpaper, null);
2153             }
2154         }
2155     }
2156
2157     private void notifyCallbacksLocked(WallpaperData wallpaper) {
2158         final int n = wallpaper.callbacks.beginBroadcast();
2159         for (int i = 0; i < n; i++) {
2160             try {
2161                 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
2162             } catch (RemoteException e) {
2163
2164                 // The RemoteCallbackList will take care of removing
2165                 // the dead object for us.
2166             }
2167         }
2168         wallpaper.callbacks.finishBroadcast();
2169
2170         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
2171         mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
2172     }
2173
2174     private void checkPermission(String permission) {
2175         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
2176             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
2177                     + ", must have permission " + permission);
2178         }
2179     }
2180
2181     /**
2182      * Certain user types do not support wallpapers (e.g. managed profiles). The check is
2183      * implemented through through the OP_WRITE_WALLPAPER AppOp.
2184      */
2185     public boolean isWallpaperSupported(String callingPackage) {
2186         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
2187                 callingPackage) == AppOpsManager.MODE_ALLOWED;
2188     }
2189
2190     @Override
2191     public boolean isSetWallpaperAllowed(String callingPackage) {
2192         final PackageManager pm = mContext.getPackageManager();
2193         String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
2194         boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
2195         if (!uidMatchPackage) {
2196             return false;   // callingPackage was faked.
2197         }
2198
2199         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
2200         if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
2201             return true;
2202         }
2203         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
2204         return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
2205     }
2206
2207     @Override
2208     public boolean isWallpaperBackupEligible(int which, int userId) {
2209         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2210             throw new SecurityException("Only the system may call isWallpaperBackupEligible");
2211         }
2212
2213         WallpaperData wallpaper = (which == FLAG_LOCK)
2214                 ? mLockWallpaperMap.get(userId)
2215                 : mWallpaperMap.get(userId);
2216         return (wallpaper != null) ? wallpaper.allowBackup : false;
2217     }
2218
2219     private static JournaledFile makeJournaledFile(int userId) {
2220         final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
2221         return new JournaledFile(new File(base), new File(base + ".tmp"));
2222     }
2223
2224     private void saveSettingsLocked(int userId) {
2225         JournaledFile journal = makeJournaledFile(userId);
2226         FileOutputStream fstream = null;
2227         BufferedOutputStream stream = null;
2228         try {
2229             XmlSerializer out = new FastXmlSerializer();
2230             fstream = new FileOutputStream(journal.chooseForWrite(), false);
2231             stream = new BufferedOutputStream(fstream);
2232             out.setOutput(stream, StandardCharsets.UTF_8.name());
2233             out.startDocument(null, true);
2234
2235             WallpaperData wallpaper;
2236
2237             wallpaper = mWallpaperMap.get(userId);
2238             if (wallpaper != null) {
2239                 writeWallpaperAttributes(out, "wp", wallpaper);
2240             }
2241             wallpaper = mLockWallpaperMap.get(userId);
2242             if (wallpaper != null) {
2243                 writeWallpaperAttributes(out, "kwp", wallpaper);
2244             }
2245
2246             out.endDocument();
2247
2248             stream.flush(); // also flushes fstream
2249             FileUtils.sync(fstream);
2250             stream.close(); // also closes fstream
2251             journal.commit();
2252         } catch (IOException e) {
2253             IoUtils.closeQuietly(stream);
2254             journal.rollback();
2255         }
2256     }
2257
2258     private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
2259             throws IllegalArgumentException, IllegalStateException, IOException {
2260         if (DEBUG) {
2261             Slog.v(TAG, "writeWallpaperAttributes");
2262         }
2263         out.startTag(null, tag);
2264         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
2265         out.attribute(null, "width", Integer.toString(wallpaper.width));
2266         out.attribute(null, "height", Integer.toString(wallpaper.height));
2267
2268         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
2269         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
2270         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
2271         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
2272
2273         if (wallpaper.padding.left != 0) {
2274             out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
2275         }
2276         if (wallpaper.padding.top != 0) {
2277             out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
2278         }
2279         if (wallpaper.padding.right != 0) {
2280             out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
2281         }
2282         if (wallpaper.padding.bottom != 0) {
2283             out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
2284         }
2285
2286         if (wallpaper.primaryColors != null) {
2287             int colorsCount = wallpaper.primaryColors.getMainColors().size();
2288             out.attribute(null, "colorsCount", Integer.toString(colorsCount));
2289             if (colorsCount > 0) {
2290                 for (int i = 0; i < colorsCount; i++) {
2291                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
2292                     out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
2293                 }
2294             }
2295             out.attribute(null, "colorHints",
2296                     Integer.toString(wallpaper.primaryColors.getColorHints()));
2297         }
2298
2299         out.attribute(null, "name", wallpaper.name);
2300         if (wallpaper.wallpaperComponent != null
2301                 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
2302             out.attribute(null, "component",
2303                     wallpaper.wallpaperComponent.flattenToShortString());
2304         }
2305
2306         if (wallpaper.allowBackup) {
2307             out.attribute(null, "backup", "true");
2308         }
2309
2310         out.endTag(null, tag);
2311     }
2312
2313     private void migrateFromOld() {
2314         // Pre-N, what existed is the one we're now using as the display crop
2315         File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
2316         // In the very-long-ago, imagery lived with the settings app
2317         File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
2318         File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
2319
2320         // Migrations from earlier wallpaper image storage schemas
2321         if (preNWallpaper.exists()) {
2322             if (!newWallpaper.exists()) {
2323                 // we've got the 'wallpaper' crop file but not the nominal source image,
2324                 // so do the simple "just take everything" straight copy of legacy data
2325                 if (DEBUG) {
2326                     Slog.i(TAG, "Migrating wallpaper schema");
2327                 }
2328                 FileUtils.copyFile(preNWallpaper, newWallpaper);
2329             } // else we're in the usual modern case: both source & crop exist
2330         } else if (originalWallpaper.exists()) {
2331             // VERY old schema; make sure things exist and are in the right place
2332             if (DEBUG) {
2333                 Slog.i(TAG, "Migrating antique wallpaper schema");
2334             }
2335             File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
2336             if (oldInfo.exists()) {
2337                 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
2338                 oldInfo.renameTo(newInfo);
2339             }
2340
2341             FileUtils.copyFile(originalWallpaper, preNWallpaper);
2342             originalWallpaper.renameTo(newWallpaper);
2343         }
2344     }
2345
2346     private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
2347         String value = parser.getAttributeValue(null, name);
2348         if (value == null) {
2349             return defValue;
2350         }
2351         return Integer.parseInt(value);
2352     }
2353
2354     /**
2355      * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
2356      * happen during user switch.  The async user switch observer may not have received
2357      * the event yet.  We use this safe method when we don't care about this ordering and just
2358      * want to update the data.  The data is going to be applied when the user switch observer
2359      * is eventually executed.
2360      */
2361     private WallpaperData getWallpaperSafeLocked(int userId, int which) {
2362         // We're setting either just system (work with the system wallpaper),
2363         // both (also work with the system wallpaper), or just the lock
2364         // wallpaper (update against the existing lock wallpaper if any).
2365         // Combined or just-system operations use the 'system' WallpaperData
2366         // for this use; lock-only operations use the dedicated one.
2367         final SparseArray<WallpaperData> whichSet =
2368                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2369         WallpaperData wallpaper = whichSet.get(userId);
2370         if (wallpaper == null) {
2371             // common case, this is the first lookup post-boot of the system or
2372             // unified lock, so we bring up the saved state lazily now and recheck.
2373             loadSettingsLocked(userId, false);
2374             wallpaper = whichSet.get(userId);
2375             // if it's still null here, this is a lock-only operation and there is not
2376             // yet a lock-only wallpaper set for this user, so we need to establish
2377             // it now.
2378             if (wallpaper == null) {
2379                 if (which == FLAG_LOCK) {
2380                     wallpaper = new WallpaperData(userId,
2381                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2382                     mLockWallpaperMap.put(userId, wallpaper);
2383                     ensureSaneWallpaperData(wallpaper);
2384                 } else {
2385                     // sanity fallback: we're in bad shape, but establishing a known
2386                     // valid system+lock WallpaperData will keep us from dying.
2387                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
2388                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2389                     mWallpaperMap.put(userId, wallpaper);
2390                     ensureSaneWallpaperData(wallpaper);
2391                 }
2392             }
2393         }
2394         return wallpaper;
2395     }
2396
2397     private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
2398         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
2399
2400         JournaledFile journal = makeJournaledFile(userId);
2401         FileInputStream stream = null;
2402         File file = journal.chooseForRead();
2403
2404         WallpaperData wallpaper = mWallpaperMap.get(userId);
2405         if (wallpaper == null) {
2406             // Do this once per boot
2407             migrateFromOld();
2408
2409             wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2410             wallpaper.allowBackup = true;
2411             mWallpaperMap.put(userId, wallpaper);
2412             if (!wallpaper.cropExists()) {
2413                 if (wallpaper.sourceExists()) {
2414                     generateCrop(wallpaper);
2415                 } else {
2416                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
2417                 }
2418             }
2419         }
2420         boolean success = false;
2421         try {
2422             stream = new FileInputStream(file);
2423             XmlPullParser parser = Xml.newPullParser();
2424             parser.setInput(stream, StandardCharsets.UTF_8.name());
2425
2426             int type;
2427             do {
2428                 type = parser.next();
2429                 if (type == XmlPullParser.START_TAG) {
2430                     String tag = parser.getName();
2431                     if ("wp".equals(tag)) {
2432                         // Common to system + lock wallpapers
2433                         parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
2434
2435                         // A system wallpaper might also be a live wallpaper
2436                         String comp = parser.getAttributeValue(null, "component");
2437                         wallpaper.nextWallpaperComponent = comp != null
2438                                 ? ComponentName.unflattenFromString(comp)
2439                                 : null;
2440                         if (wallpaper.nextWallpaperComponent == null
2441                                 || "android".equals(wallpaper.nextWallpaperComponent
2442                                         .getPackageName())) {
2443                             wallpaper.nextWallpaperComponent = mImageWallpaper;
2444                         }
2445
2446                         if (DEBUG) {
2447                             Slog.v(TAG, "mWidth:" + wallpaper.width);
2448                             Slog.v(TAG, "mHeight:" + wallpaper.height);
2449                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
2450                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
2451                             Slog.v(TAG, "mName:" + wallpaper.name);
2452                             Slog.v(TAG, "mNextWallpaperComponent:"
2453                                     + wallpaper.nextWallpaperComponent);
2454                         }
2455                     } else if ("kwp".equals(tag)) {
2456                         // keyguard-specific wallpaper for this user
2457                         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
2458                         if (lockWallpaper == null) {
2459                             lockWallpaper = new WallpaperData(userId,
2460                                     WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2461                             mLockWallpaperMap.put(userId, lockWallpaper);
2462                         }
2463                         parseWallpaperAttributes(parser, lockWallpaper, false);
2464                     }
2465                 }
2466             } while (type != XmlPullParser.END_DOCUMENT);
2467             success = true;
2468         } catch (FileNotFoundException e) {
2469             Slog.w(TAG, "no current wallpaper -- first boot?");
2470         } catch (NullPointerException e) {
2471             Slog.w(TAG, "failed parsing " + file + " " + e);
2472         } catch (NumberFormatException e) {
2473             Slog.w(TAG, "failed parsing " + file + " " + e);
2474         } catch (XmlPullParserException e) {
2475             Slog.w(TAG, "failed parsing " + file + " " + e);
2476         } catch (IOException e) {
2477             Slog.w(TAG, "failed parsing " + file + " " + e);
2478         } catch (IndexOutOfBoundsException e) {
2479             Slog.w(TAG, "failed parsing " + file + " " + e);
2480         }
2481         IoUtils.closeQuietly(stream);
2482
2483         if (!success) {
2484             wallpaper.width = -1;
2485             wallpaper.height = -1;
2486             wallpaper.cropHint.set(0, 0, 0, 0);
2487             wallpaper.padding.set(0, 0, 0, 0);
2488             wallpaper.name = "";
2489
2490             mLockWallpaperMap.remove(userId);
2491         } else {
2492             if (wallpaper.wallpaperId <= 0) {
2493                 wallpaper.wallpaperId = makeWallpaperIdLocked();
2494                 if (DEBUG) {
2495                     Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
2496                             + "); now " + wallpaper.wallpaperId);
2497                 }
2498             }
2499         }
2500
2501         ensureSaneWallpaperData(wallpaper);
2502         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
2503         if (lockWallpaper != null) {
2504             ensureSaneWallpaperData(lockWallpaper);
2505         }
2506     }
2507
2508     private void ensureSaneWallpaperData(WallpaperData wallpaper) {
2509         // We always want to have some reasonable width hint.
2510         int baseSize = getMaximumSizeDimension();
2511         if (wallpaper.width < baseSize) {
2512             wallpaper.width = baseSize;
2513         }
2514         if (wallpaper.height < baseSize) {
2515             wallpaper.height = baseSize;
2516         }
2517         // and crop, if not previously specified
2518         if (wallpaper.cropHint.width() <= 0
2519                 || wallpaper.cropHint.height() <= 0) {
2520             wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
2521         }
2522     }
2523
2524     private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
2525             boolean keepDimensionHints) {
2526         final String idString = parser.getAttributeValue(null, "id");
2527         if (idString != null) {
2528             final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
2529             if (id > mWallpaperId) {
2530                 mWallpaperId = id;
2531             }
2532         } else {
2533             wallpaper.wallpaperId = makeWallpaperIdLocked();
2534         }
2535
2536         if (!keepDimensionHints) {
2537             wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
2538             wallpaper.height = Integer.parseInt(parser
2539                     .getAttributeValue(null, "height"));
2540         }
2541         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
2542         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
2543         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
2544         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
2545         wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
2546         wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
2547         wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
2548         wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
2549         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
2550         if (colorsCount > 0) {
2551             Color primary = null, secondary = null, tertiary = null;
2552             for (int i = 0; i < colorsCount; i++) {
2553                 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
2554                 if (i == 0) {
2555                     primary = color;
2556                 } else if (i == 1) {
2557                     secondary = color;
2558                 } else if (i == 2) {
2559                     tertiary = color;
2560                 } else {
2561                     break;
2562                 }
2563             }
2564             int colorHints = getAttributeInt(parser, "colorHints", 0);
2565             wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
2566         }
2567         wallpaper.name = parser.getAttributeValue(null, "name");
2568         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
2569     }
2570
2571     private int getMaximumSizeDimension() {
2572         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
2573         Display d = wm.getDefaultDisplay();
2574         return d.getMaximumSizeDimension();
2575     }
2576
2577     // Called by SystemBackupAgent after files are restored to disk.
2578     public void settingsRestored() {
2579         // Verify caller is the system
2580         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2581             throw new RuntimeException("settingsRestored() can only be called from the system process");
2582         }
2583         // TODO: If necessary, make it work for secondary users as well. This currently assumes
2584         // restores only to the primary user
2585         if (DEBUG) Slog.v(TAG, "settingsRestored");
2586         WallpaperData wallpaper = null;
2587         boolean success = false;
2588         synchronized (mLock) {
2589             loadSettingsLocked(UserHandle.USER_SYSTEM, false);
2590             wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
2591             wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
2592             wallpaper.allowBackup = true;   // by definition if it was restored
2593             if (wallpaper.nextWallpaperComponent != null
2594                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
2595                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
2596                         wallpaper, null)) {
2597                     // No such live wallpaper or other failure; fall back to the default
2598                     // live wallpaper (since the profile being restored indicated that the
2599                     // user had selected a live rather than static one).
2600                     bindWallpaperComponentLocked(null, false, false, wallpaper, null);
2601                 }
2602                 success = true;
2603             } else {
2604                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
2605                 // use the default.
2606                 if ("".equals(wallpaper.name)) {
2607                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
2608                     success = true;
2609                 } else {
2610                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
2611                     success = restoreNamedResourceLocked(wallpaper);
2612                 }
2613                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
2614                         + " id=" + wallpaper.wallpaperId);
2615                 if (success) {
2616                     generateCrop(wallpaper);    // based on the new image + metadata
2617                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
2618                             wallpaper, null);
2619                 }
2620             }
2621         }
2622
2623         if (!success) {
2624             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
2625             wallpaper.name = "";
2626             getWallpaperDir(UserHandle.USER_SYSTEM).delete();
2627         }
2628
2629         synchronized (mLock) {
2630             saveSettingsLocked(UserHandle.USER_SYSTEM);
2631         }
2632     }
2633
2634     // Restore the named resource bitmap to both source + crop files
2635     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
2636         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
2637             String resName = wallpaper.name.substring(4);
2638
2639             String pkg = null;
2640             int colon = resName.indexOf(':');
2641             if (colon > 0) {
2642                 pkg = resName.substring(0, colon);
2643             }
2644
2645             String ident = null;
2646             int slash = resName.lastIndexOf('/');
2647             if (slash > 0) {
2648                 ident = resName.substring(slash+1);
2649             }
2650
2651             String type = null;
2652             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
2653                 type = resName.substring(colon+1, slash);
2654             }
2655
2656             if (pkg != null && ident != null && type != null) {
2657                 int resId = -1;
2658                 InputStream res = null;
2659                 FileOutputStream fos = null;
2660                 FileOutputStream cos = null;
2661                 try {
2662                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
2663                     Resources r = c.getResources();
2664                     resId = r.getIdentifier(resName, null, null);
2665                     if (resId == 0) {
2666                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
2667                                 + " ident=" + ident);
2668                         return false;
2669                     }
2670
2671                     res = r.openRawResource(resId);
2672                     if (wallpaper.wallpaperFile.exists()) {
2673                         wallpaper.wallpaperFile.delete();
2674                         wallpaper.cropFile.delete();
2675                     }
2676                     fos = new FileOutputStream(wallpaper.wallpaperFile);
2677                     cos = new FileOutputStream(wallpaper.cropFile);
2678
2679                     byte[] buffer = new byte[32768];
2680                     int amt;
2681                     while ((amt=res.read(buffer)) > 0) {
2682                         fos.write(buffer, 0, amt);
2683                         cos.write(buffer, 0, amt);
2684                     }
2685                     // mWallpaperObserver will notice the close and send the change broadcast
2686
2687                     Slog.v(TAG, "Restored wallpaper: " + resName);
2688                     return true;
2689                 } catch (NameNotFoundException e) {
2690                     Slog.e(TAG, "Package name " + pkg + " not found");
2691                 } catch (Resources.NotFoundException e) {
2692                     Slog.e(TAG, "Resource not found: " + resId);
2693                 } catch (IOException e) {
2694                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
2695                 } finally {
2696                     IoUtils.closeQuietly(res);
2697                     if (fos != null) {
2698                         FileUtils.sync(fos);
2699                     }
2700                     if (cos != null) {
2701                         FileUtils.sync(cos);
2702                     }
2703                     IoUtils.closeQuietly(fos);
2704                     IoUtils.closeQuietly(cos);
2705                 }
2706             }
2707         }
2708         return false;
2709     }
2710
2711     @Override
2712     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2713         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
2714
2715         synchronized (mLock) {
2716             pw.println("System wallpaper state:");
2717             for (int i = 0; i < mWallpaperMap.size(); i++) {
2718                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
2719                 pw.print(" User "); pw.print(wallpaper.userId);
2720                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
2721                 pw.print("  mWidth=");
2722                     pw.print(wallpaper.width);
2723                     pw.print(" mHeight=");
2724                     pw.println(wallpaper.height);
2725                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
2726                 pw.print("  mPadding="); pw.println(wallpaper.padding);
2727                 pw.print("  mName=");  pw.println(wallpaper.name);
2728                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
2729                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
2730                 if (wallpaper.connection != null) {
2731                     WallpaperConnection conn = wallpaper.connection;
2732                     pw.print("  Wallpaper connection ");
2733                     pw.print(conn);
2734                     pw.println(":");
2735                     if (conn.mInfo != null) {
2736                         pw.print("    mInfo.component=");
2737                         pw.println(conn.mInfo.getComponent());
2738                     }
2739                     pw.print("    mToken=");
2740                     pw.println(conn.mToken);
2741                     pw.print("    mService=");
2742                     pw.println(conn.mService);
2743                     pw.print("    mEngine=");
2744                     pw.println(conn.mEngine);
2745                     pw.print("    mLastDiedTime=");
2746                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
2747                 }
2748             }
2749             pw.println("Lock wallpaper state:");
2750             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
2751                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
2752                 pw.print(" User "); pw.print(wallpaper.userId);
2753                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
2754                 pw.print("  mWidth="); pw.print(wallpaper.width);
2755                     pw.print(" mHeight="); pw.println(wallpaper.height);
2756                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
2757                 pw.print("  mPadding="); pw.println(wallpaper.padding);
2758                 pw.print("  mName=");  pw.println(wallpaper.name);
2759                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
2760             }
2761
2762         }
2763     }
2764 }