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
17package com.android.server;
18
19import static android.os.FileObserver.*;
20import static android.os.ParcelFileDescriptor.*;
21
22import android.app.IWallpaperManager;
23import android.app.IWallpaperManagerCallback;
24import android.app.PendingIntent;
25import android.app.WallpaperInfo;
26import android.backup.BackupManager;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
30import android.content.ServiceConnection;
31import android.content.pm.PackageManager;
32import android.content.pm.ResolveInfo;
33import android.content.pm.ServiceInfo;
34import android.content.pm.PackageManager.NameNotFoundException;
35import android.content.res.Resources;
36import android.os.Binder;
37import android.os.Bundle;
38import android.os.IBinder;
39import android.os.RemoteException;
40import android.os.FileObserver;
41import android.os.ParcelFileDescriptor;
42import android.os.RemoteCallbackList;
43import android.os.ServiceManager;
44import android.os.SystemClock;
45import android.service.wallpaper.IWallpaperConnection;
46import android.service.wallpaper.IWallpaperEngine;
47import android.service.wallpaper.IWallpaperService;
48import android.service.wallpaper.WallpaperService;
49import android.util.Log;
50import android.util.Xml;
51import android.view.IWindowManager;
52import android.view.WindowManager;
53
54import java.io.FileDescriptor;
55import java.io.IOException;
56import java.io.InputStream;
57import java.io.File;
58import java.io.FileNotFoundException;
59import java.io.FileInputStream;
60import java.io.FileOutputStream;
61import java.io.PrintWriter;
62import java.util.List;
63
64import org.xmlpull.v1.XmlPullParser;
65import org.xmlpull.v1.XmlPullParserException;
66import org.xmlpull.v1.XmlSerializer;
67
68import com.android.internal.service.wallpaper.ImageWallpaper;
69import com.android.internal.util.FastXmlSerializer;
70
71class WallpaperManagerService extends IWallpaperManager.Stub {
72    static final String TAG = "WallpaperService";
73    static final boolean DEBUG = false;
74
75    Object mLock = new Object();
76
77    /**
78     * Minimum time between crashes of a wallpaper service for us to consider
79     * restarting it vs. just reverting to the static wallpaper.
80     */
81    static final long MIN_WALLPAPER_CRASH_TIME = 10000;
82
83    static final File WALLPAPER_DIR = new File(
84            "/data/data/com.android.settings/files");
85    static final String WALLPAPER = "wallpaper";
86    static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
87
88    /**
89     * List of callbacks registered they should each be notified
90     * when the wallpaper is changed.
91     */
92    private final RemoteCallbackList<IWallpaperManagerCallback> mCallbacks
93            = new RemoteCallbackList<IWallpaperManagerCallback>();
94
95    /**
96     * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
97     * that the wallpaper has changed. The CREATE is triggered when there is no
98     * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
99     * everytime the wallpaper is changed.
100     */
101    private final FileObserver mWallpaperObserver = new FileObserver(
102            WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) {
103                @Override
104                public void onEvent(int event, String path) {
105                    if (path == null) {
106                        return;
107                    }
108                    synchronized (mLock) {
109                        // changing the wallpaper means we'll need to back up the new one
110                        long origId = Binder.clearCallingIdentity();
111                        BackupManager bm = new BackupManager(mContext);
112                        bm.dataChanged();
113                        Binder.restoreCallingIdentity(origId);
114
115                        File changedFile = new File(WALLPAPER_DIR, path);
116                        if (WALLPAPER_FILE.equals(changedFile)) {
117                            notifyCallbacksLocked();
118                        }
119                    }
120                }
121            };
122
123    final Context mContext;
124    final IWindowManager mIWindowManager;
125
126    int mWidth = -1;
127    int mHeight = -1;
128
129    /**
130     * Resource name if using a picture from the wallpaper gallery
131     */
132    String mName = "";
133
134    /**
135     * The component name of the currently set live wallpaper.
136     */
137    ComponentName mWallpaperComponent;
138
139    /**
140     * The component name of the wallpaper that should be set next.
141     */
142    ComponentName mNextWallpaperComponent;
143
144    /**
145     * Name of the component used to display bitmap wallpapers from either the gallery or
146     * built-in wallpapers.
147     */
148    ComponentName mImageWallpaperComponent = new ComponentName("android",
149            ImageWallpaper.class.getName());
150
151    WallpaperConnection mWallpaperConnection;
152    long mLastDiedTime;
153
154    class WallpaperConnection extends IWallpaperConnection.Stub
155            implements ServiceConnection {
156        final WallpaperInfo mInfo;
157        final Binder mToken = new Binder();
158        IWallpaperService mService;
159        IWallpaperEngine mEngine;
160
161        public WallpaperConnection(WallpaperInfo info) {
162            mInfo = info;
163        }
164
165        public void onServiceConnected(ComponentName name, IBinder service) {
166            synchronized (mLock) {
167                if (mWallpaperConnection == this) {
168                    mService = IWallpaperService.Stub.asInterface(service);
169                    attachServiceLocked(this);
170                    // XXX should probably do saveSettingsLocked() later
171                    // when we have an engine, but I'm not sure about
172                    // locking there and anyway we always need to be able to
173                    // recover if there is something wrong.
174                    saveSettingsLocked();
175                }
176            }
177        }
178
179        public void onServiceDisconnected(ComponentName name) {
180            synchronized (mLock) {
181                mService = null;
182                mEngine = null;
183                if (mWallpaperConnection == this) {
184                    Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
185                    if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
186                            < SystemClock.uptimeMillis()) {
187                        Log.w(TAG, "Reverting to built-in wallpaper!");
188                        bindWallpaperComponentLocked(null);
189                    }
190                }
191            }
192        }
193
194        public void attachEngine(IWallpaperEngine engine) {
195            mEngine = engine;
196        }
197
198        public ParcelFileDescriptor setWallpaper(String name) {
199            synchronized (mLock) {
200                if (mWallpaperConnection == this) {
201                    return updateWallpaperBitmapLocked(name);
202                }
203                return null;
204            }
205        }
206    }
207
208    public WallpaperManagerService(Context context) {
209        if (DEBUG) Log.v(TAG, "WallpaperService startup");
210        mContext = context;
211        mIWindowManager = IWindowManager.Stub.asInterface(
212                ServiceManager.getService(Context.WINDOW_SERVICE));
213        WALLPAPER_DIR.mkdirs();
214        loadSettingsLocked();
215        mWallpaperObserver.startWatching();
216    }
217
218    @Override
219    protected void finalize() throws Throwable {
220        super.finalize();
221        mWallpaperObserver.stopWatching();
222    }
223
224    public void systemReady() {
225        if (DEBUG) Log.v(TAG, "systemReady");
226        synchronized (mLock) {
227            try {
228                bindWallpaperComponentLocked(mNextWallpaperComponent);
229            } catch (RuntimeException e) {
230                Log.w(TAG, "Failure starting previous wallpaper", e);
231                try {
232                    bindWallpaperComponentLocked(null);
233                } catch (RuntimeException e2) {
234                    Log.w(TAG, "Failure starting default wallpaper", e2);
235                    clearWallpaperComponentLocked();
236                }
237            }
238        }
239    }
240
241    public void clearWallpaper() {
242        if (DEBUG) Log.v(TAG, "clearWallpaper");
243        synchronized (mLock) {
244            File f = WALLPAPER_FILE;
245            if (f.exists()) {
246                f.delete();
247            }
248            final long ident = Binder.clearCallingIdentity();
249            try {
250                bindWallpaperComponentLocked(null);
251            } finally {
252                Binder.restoreCallingIdentity(ident);
253            }
254        }
255    }
256
257    public void setDimensionHints(int width, int height) throws RemoteException {
258        checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
259
260        if (width <= 0 || height <= 0) {
261            throw new IllegalArgumentException("width and height must be > 0");
262        }
263
264        synchronized (mLock) {
265            if (width != mWidth || height != mHeight) {
266                mWidth = width;
267                mHeight = height;
268                saveSettingsLocked();
269                if (mWallpaperConnection != null) {
270                    if (mWallpaperConnection.mEngine != null) {
271                        try {
272                            mWallpaperConnection.mEngine.setDesiredSize(
273                                    width, height);
274                        } catch (RemoteException e) {
275                        }
276                        notifyCallbacksLocked();
277                    }
278                }
279            }
280        }
281    }
282
283    public int getWidthHint() throws RemoteException {
284        synchronized (mLock) {
285            return mWidth;
286        }
287    }
288
289    public int getHeightHint() throws RemoteException {
290        synchronized (mLock) {
291            return mHeight;
292        }
293    }
294
295    public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
296            Bundle outParams) {
297        synchronized (mLock) {
298            try {
299                if (outParams != null) {
300                    outParams.putInt("width", mWidth);
301                    outParams.putInt("height", mHeight);
302                }
303                mCallbacks.register(cb);
304                File f = WALLPAPER_FILE;
305                if (!f.exists()) {
306                    return null;
307                }
308                return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
309            } catch (FileNotFoundException e) {
310                /* Shouldn't happen as we check to see if the file exists */
311                Log.w(TAG, "Error getting wallpaper", e);
312            }
313            return null;
314        }
315    }
316
317    public WallpaperInfo getWallpaperInfo() {
318        synchronized (mLock) {
319            if (mWallpaperConnection != null) {
320                return mWallpaperConnection.mInfo;
321            }
322            return null;
323        }
324    }
325
326    public ParcelFileDescriptor setWallpaper(String name) {
327        if (DEBUG) Log.v(TAG, "setWallpaper");
328
329        checkPermission(android.Manifest.permission.SET_WALLPAPER);
330        synchronized (mLock) {
331            final long ident = Binder.clearCallingIdentity();
332            try {
333                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
334                if (pfd != null) {
335                    // Bind the wallpaper to an ImageWallpaper
336                    bindWallpaperComponentLocked(mImageWallpaperComponent);
337                    saveSettingsLocked();
338                }
339                return pfd;
340            } finally {
341                Binder.restoreCallingIdentity(ident);
342            }
343        }
344    }
345
346    ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
347        if (name == null) name = "";
348        try {
349            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
350                    MODE_CREATE|MODE_READ_WRITE);
351            mName = name;
352            return fd;
353        } catch (FileNotFoundException e) {
354            Log.w(TAG, "Error setting wallpaper", e);
355        }
356        return null;
357    }
358
359    public void setWallpaperComponent(ComponentName name) {
360        if (DEBUG) Log.v(TAG, "setWallpaperComponent name=" + name);
361        checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
362        synchronized (mLock) {
363            final long ident = Binder.clearCallingIdentity();
364            try {
365                bindWallpaperComponentLocked(name);
366            } finally {
367                Binder.restoreCallingIdentity(ident);
368            }
369        }
370    }
371
372    void bindWallpaperComponentLocked(ComponentName componentName) {
373        if (DEBUG) Log.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
374
375        // Has the component changed?
376        if (mWallpaperConnection != null) {
377            if (mWallpaperComponent == null) {
378                if (componentName == null) {
379                    if (DEBUG) Log.v(TAG, "bindWallpaperComponentLocked: still using default");
380                    // Still using default wallpaper.
381                    return;
382                }
383            } else if (mWallpaperComponent.equals(componentName)) {
384                // Changing to same wallpaper.
385                if (DEBUG) Log.v(TAG, "same wallpaper");
386                return;
387            }
388        }
389
390        try {
391            if (componentName == null) {
392                String defaultComponent =
393                    mContext.getString(com.android.internal.R.string.default_wallpaper_component);
394                if (defaultComponent != null) {
395                    // See if there is a default wallpaper component specified
396                    componentName = ComponentName.unflattenFromString(defaultComponent);
397                    if (DEBUG) Log.v(TAG, "Use default component walpaper:" + componentName);
398                }
399                if (componentName == null) {
400                    // Fall back to static image wallpaper
401                    componentName = mImageWallpaperComponent;
402                    //clearWallpaperComponentLocked();
403                    //return;
404                    if (DEBUG) Log.v(TAG, "Using image wallpaper");
405                }
406            }
407            ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
408                    PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
409            if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
410                throw new SecurityException("Selected service does not require "
411                        + android.Manifest.permission.BIND_WALLPAPER
412                        + ": " + componentName);
413            }
414
415            WallpaperInfo wi = null;
416
417            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
418            if (componentName != null && !componentName.equals(mImageWallpaperComponent)) {
419                // Make sure the selected service is actually a wallpaper service.
420                List<ResolveInfo> ris = mContext.getPackageManager()
421                        .queryIntentServices(intent, PackageManager.GET_META_DATA);
422                for (int i=0; i<ris.size(); i++) {
423                    ServiceInfo rsi = ris.get(i).serviceInfo;
424                    if (rsi.name.equals(si.name) &&
425                            rsi.packageName.equals(si.packageName)) {
426                        try {
427                            wi = new WallpaperInfo(mContext, ris.get(i));
428                        } catch (XmlPullParserException e) {
429                            throw new IllegalArgumentException(e);
430                        } catch (IOException e) {
431                            throw new IllegalArgumentException(e);
432                        }
433                        break;
434                    }
435                }
436                if (wi == null) {
437                    throw new SecurityException("Selected service is not a wallpaper: "
438                            + componentName);
439                }
440            }
441
442            // Bind the service!
443            if (DEBUG) Log.v(TAG, "Binding to:" + componentName);
444            WallpaperConnection newConn = new WallpaperConnection(wi);
445            intent.setComponent(componentName);
446            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
447                    com.android.internal.R.string.wallpaper_binding_label);
448            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
449                    mContext, 0,
450                    Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
451                            mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
452                            0));
453            if (!mContext.bindService(intent, newConn,
454                    Context.BIND_AUTO_CREATE)) {
455                throw new IllegalArgumentException("Unable to bind service: "
456                        + componentName);
457            }
458
459            clearWallpaperComponentLocked();
460            mWallpaperComponent = componentName;
461            mWallpaperConnection = newConn;
462            mLastDiedTime = SystemClock.uptimeMillis();
463            try {
464                if (DEBUG) Log.v(TAG, "Adding window token: " + newConn.mToken);
465                mIWindowManager.addWindowToken(newConn.mToken,
466                        WindowManager.LayoutParams.TYPE_WALLPAPER);
467            } catch (RemoteException e) {
468            }
469
470        } catch (PackageManager.NameNotFoundException e) {
471            throw new IllegalArgumentException("Unknown component " + componentName);
472        }
473    }
474
475    void clearWallpaperComponentLocked() {
476        mWallpaperComponent = null;
477        if (mWallpaperConnection != null) {
478            if (mWallpaperConnection.mEngine != null) {
479                try {
480                    mWallpaperConnection.mEngine.destroy();
481                } catch (RemoteException e) {
482                }
483            }
484            mContext.unbindService(mWallpaperConnection);
485            try {
486                if (DEBUG) Log.v(TAG, "Removing window token: "
487                        + mWallpaperConnection.mToken);
488                mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
489            } catch (RemoteException e) {
490            }
491            mWallpaperConnection = null;
492        }
493    }
494
495    void attachServiceLocked(WallpaperConnection conn) {
496        try {
497            conn.mService.attach(conn, conn.mToken,
498                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
499                    mWidth, mHeight);
500        } catch (RemoteException e) {
501            Log.w(TAG, "Failed attaching wallpaper; clearing", e);
502            bindWallpaperComponentLocked(null);
503        }
504    }
505
506    private void notifyCallbacksLocked() {
507        final int n = mCallbacks.beginBroadcast();
508        for (int i = 0; i < n; i++) {
509            try {
510                mCallbacks.getBroadcastItem(i).onWallpaperChanged();
511            } catch (RemoteException e) {
512
513                // The RemoteCallbackList will take care of removing
514                // the dead object for us.
515            }
516        }
517        mCallbacks.finishBroadcast();
518        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
519        mContext.sendBroadcast(intent);
520    }
521
522    private void checkPermission(String permission) {
523        if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
524            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
525                    + ", must have permission " + permission);
526        }
527    }
528
529    private static JournaledFile makeJournaledFile() {
530        final String base = "/data/system/wallpaper_info.xml";
531        return new JournaledFile(new File(base), new File(base + ".tmp"));
532    }
533
534    private void saveSettingsLocked() {
535        JournaledFile journal = makeJournaledFile();
536        FileOutputStream stream = null;
537        try {
538            stream = new FileOutputStream(journal.chooseForWrite(), false);
539            XmlSerializer out = new FastXmlSerializer();
540            out.setOutput(stream, "utf-8");
541            out.startDocument(null, true);
542
543            out.startTag(null, "wp");
544            out.attribute(null, "width", Integer.toString(mWidth));
545            out.attribute(null, "height", Integer.toString(mHeight));
546            out.attribute(null, "name", mName);
547            if (mWallpaperComponent != null) {
548                out.attribute(null, "component",
549                        mWallpaperComponent.flattenToShortString());
550            }
551            out.endTag(null, "wp");
552
553            out.endDocument();
554            stream.close();
555            journal.commit();
556        } catch (IOException e) {
557            try {
558                if (stream != null) {
559                    stream.close();
560                }
561            } catch (IOException ex) {
562                // Ignore
563            }
564            journal.rollback();
565        }
566    }
567
568    private void loadSettingsLocked() {
569        if (DEBUG) Log.v(TAG, "loadSettingsLocked");
570
571        JournaledFile journal = makeJournaledFile();
572        FileInputStream stream = null;
573        File file = journal.chooseForRead();
574        boolean success = false;
575        try {
576            stream = new FileInputStream(file);
577            XmlPullParser parser = Xml.newPullParser();
578            parser.setInput(stream, null);
579
580            int type;
581            do {
582                type = parser.next();
583                if (type == XmlPullParser.START_TAG) {
584                    String tag = parser.getName();
585                    if ("wp".equals(tag)) {
586                        mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
587                        mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
588                        mName = parser.getAttributeValue(null, "name");
589                        String comp = parser.getAttributeValue(null, "component");
590                        mNextWallpaperComponent = comp != null
591                                ? ComponentName.unflattenFromString(comp)
592                                : null;
593
594                        if (DEBUG) {
595                            Log.v(TAG, "mWidth:" + mWidth);
596                            Log.v(TAG, "mHeight:" + mHeight);
597                            Log.v(TAG, "mName:" + mName);
598                            Log.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent);
599                        }
600                    }
601                }
602            } while (type != XmlPullParser.END_DOCUMENT);
603            success = true;
604        } catch (NullPointerException e) {
605            Log.w(TAG, "failed parsing " + file + " " + e);
606        } catch (NumberFormatException e) {
607            Log.w(TAG, "failed parsing " + file + " " + e);
608        } catch (XmlPullParserException e) {
609            Log.w(TAG, "failed parsing " + file + " " + e);
610        } catch (IOException e) {
611            Log.w(TAG, "failed parsing " + file + " " + e);
612        } catch (IndexOutOfBoundsException e) {
613            Log.w(TAG, "failed parsing " + file + " " + e);
614        }
615        try {
616            if (stream != null) {
617                stream.close();
618            }
619        } catch (IOException e) {
620            // Ignore
621        }
622
623        if (!success) {
624            mWidth = -1;
625            mHeight = -1;
626            mName = "";
627        }
628    }
629
630    void settingsRestored() {
631        if (DEBUG) Log.v(TAG, "settingsRestored");
632
633        boolean success = false;
634        synchronized (mLock) {
635            loadSettingsLocked();
636            if (mNextWallpaperComponent != null &&
637                    !mNextWallpaperComponent.equals(mImageWallpaperComponent)) {
638                // We can't restore live wallpapers, so just go with the default
639                bindWallpaperComponentLocked(null);
640                success = true;
641            } else {
642                // If there's a wallpaper name, we use that.  If that can't be loaded, then we
643                // use the default.
644                if ("".equals(mName)) {
645                    if (DEBUG) Log.v(TAG, "settingsRestored: name is empty");
646                    success = true;
647                } else {
648                    if (DEBUG) Log.v(TAG, "settingsRestored: attempting to restore named resource");
649                    success = restoreNamedResourceLocked();
650                }
651                if (DEBUG) Log.v(TAG, "settingsRestored: success=" + success);
652                if (success) {
653                    bindWallpaperComponentLocked(mImageWallpaperComponent);
654                }
655            }
656        }
657
658        if (!success) {
659            Log.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
660            mName = "";
661            WALLPAPER_FILE.delete();
662        }
663        saveSettingsLocked();
664    }
665
666    boolean restoreNamedResourceLocked() {
667        if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
668            String resName = mName.substring(4);
669
670            String pkg = null;
671            int colon = resName.indexOf(':');
672            if (colon > 0) {
673                pkg = resName.substring(0, colon);
674            }
675
676            String ident = null;
677            int slash = resName.lastIndexOf('/');
678            if (slash > 0) {
679                ident = resName.substring(slash+1);
680            }
681
682            String type = null;
683            if (colon > 0 && slash > 0 && (slash-colon) > 1) {
684                type = resName.substring(colon+1, slash);
685            }
686
687            if (pkg != null && ident != null && type != null) {
688                int resId = -1;
689                InputStream res = null;
690                FileOutputStream fos = null;
691                try {
692                    Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
693                    Resources r = c.getResources();
694                    resId = r.getIdentifier(resName, null, null);
695                    if (resId == 0) {
696                        Log.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
697                                + " ident=" + ident);
698                        return false;
699                    }
700
701                    res = r.openRawResource(resId);
702                    fos = new FileOutputStream(WALLPAPER_FILE);
703
704                    byte[] buffer = new byte[32768];
705                    int amt;
706                    while ((amt=res.read(buffer)) > 0) {
707                        fos.write(buffer, 0, amt);
708                    }
709                    // mWallpaperObserver will notice the close and send the change broadcast
710
711                    Log.v(TAG, "Restored wallpaper: " + resName);
712                    return true;
713                } catch (NameNotFoundException e) {
714                    Log.e(TAG, "Package name " + pkg + " not found");
715                } catch (Resources.NotFoundException e) {
716                    Log.e(TAG, "Resource not found: " + resId);
717                } catch (IOException e) {
718                    Log.e(TAG, "IOException while restoring wallpaper ", e);
719                } finally {
720                    if (res != null) {
721                        try {
722                            res.close();
723                        } catch (IOException ex) {}
724                    }
725                    if (fos != null) {
726                        try {
727                            fos.close();
728                        } catch (IOException ex) {}
729                    }
730                }
731            }
732        }
733        return false;
734    }
735
736    @Override
737    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
738        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
739                != PackageManager.PERMISSION_GRANTED) {
740
741            pw.println("Permission Denial: can't dump wallpaper service from from pid="
742                    + Binder.getCallingPid()
743                    + ", uid=" + Binder.getCallingUid());
744            return;
745        }
746
747        synchronized (mLock) {
748            pw.println("Current Wallpaper Service state:");
749            pw.print("  mWidth="); pw.print(mWidth);
750                    pw.print(" mHeight="); pw.println(mHeight);
751            pw.print("  mName="); pw.println(mName);
752            pw.print("  mWallpaperComponent="); pw.println(mWallpaperComponent);
753            if (mWallpaperConnection != null) {
754                WallpaperConnection conn = mWallpaperConnection;
755                pw.print("  Wallpaper connection ");
756                        pw.print(conn); pw.println(":");
757                pw.print("    mInfo.component="); pw.println(conn.mInfo.getComponent());
758                pw.print("    mToken="); pw.println(conn.mToken);
759                pw.print("    mService="); pw.println(conn.mService);
760                pw.print("    mEngine="); pw.println(conn.mEngine);
761                pw.print("    mLastDiedTime=");
762                        pw.println(mLastDiedTime - SystemClock.uptimeMillis());
763            }
764        }
765    }
766}
767