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