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