WallpaperManagerService.java revision 5dc5a00e7ebadc085ded7e29feacd17e53698486
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.ParcelFileDescriptor.*;
20
21import android.app.ActivityManagerNative;
22import android.app.AppGlobals;
23import android.app.IUserSwitchObserver;
24import android.app.IWallpaperManager;
25import android.app.IWallpaperManagerCallback;
26import android.app.PendingIntent;
27import android.app.WallpaperInfo;
28import android.app.backup.BackupManager;
29import android.app.backup.WallpaperBackupHelper;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.ServiceConnection;
36import android.content.pm.IPackageManager;
37import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
39import android.content.pm.ServiceInfo;
40import android.content.pm.PackageManager.NameNotFoundException;
41import android.content.res.Resources;
42import android.os.Binder;
43import android.os.Bundle;
44import android.os.Environment;
45import android.os.FileUtils;
46import android.os.IBinder;
47import android.os.IRemoteCallback;
48import android.os.RemoteException;
49import android.os.FileObserver;
50import android.os.ParcelFileDescriptor;
51import android.os.RemoteCallbackList;
52import android.os.SELinux;
53import android.os.ServiceManager;
54import android.os.SystemClock;
55import android.os.UserHandle;
56import android.service.wallpaper.IWallpaperConnection;
57import android.service.wallpaper.IWallpaperEngine;
58import android.service.wallpaper.IWallpaperService;
59import android.service.wallpaper.WallpaperService;
60import android.util.Slog;
61import android.util.SparseArray;
62import android.util.Xml;
63import android.view.Display;
64import android.view.IWindowManager;
65import android.view.WindowManager;
66
67import java.io.FileDescriptor;
68import java.io.IOException;
69import java.io.InputStream;
70import java.io.File;
71import java.io.FileNotFoundException;
72import java.io.FileInputStream;
73import java.io.FileOutputStream;
74import java.io.PrintWriter;
75import java.util.List;
76
77import org.xmlpull.v1.XmlPullParser;
78import org.xmlpull.v1.XmlPullParserException;
79import org.xmlpull.v1.XmlSerializer;
80
81import com.android.internal.content.PackageMonitor;
82import com.android.internal.util.FastXmlSerializer;
83import com.android.internal.util.JournaledFile;
84
85class WallpaperManagerService extends IWallpaperManager.Stub {
86    static final String TAG = "WallpaperService";
87    static final boolean DEBUG = false;
88
89    final Object mLock = new Object[0];
90
91    /**
92     * Minimum time between crashes of a wallpaper service for us to consider
93     * restarting it vs. just reverting to the static wallpaper.
94     */
95    static final long MIN_WALLPAPER_CRASH_TIME = 10000;
96    static final String WALLPAPER = "wallpaper";
97    static final String WALLPAPER_INFO = "wallpaper_info.xml";
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 class WallpaperObserver extends FileObserver {
106
107        final WallpaperData mWallpaper;
108        final File mWallpaperDir;
109        final File mWallpaperFile;
110
111        public WallpaperObserver(WallpaperData wallpaper) {
112            super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
113                    CLOSE_WRITE | DELETE | DELETE_SELF);
114            mWallpaperDir = getWallpaperDir(wallpaper.userId);
115            mWallpaper = wallpaper;
116            mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
117        }
118
119        @Override
120        public void onEvent(int event, String path) {
121            if (path == null) {
122                return;
123            }
124            synchronized (mLock) {
125                // changing the wallpaper means we'll need to back up the new one
126                long origId = Binder.clearCallingIdentity();
127                BackupManager bm = new BackupManager(mContext);
128                bm.dataChanged();
129                Binder.restoreCallingIdentity(origId);
130
131                File changedFile = new File(mWallpaperDir, path);
132                if (mWallpaperFile.equals(changedFile)) {
133                    notifyCallbacksLocked(mWallpaper);
134                    if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE
135                            || mWallpaper.imageWallpaperPending) {
136                        if (event == CLOSE_WRITE) {
137                            mWallpaper.imageWallpaperPending = false;
138                        }
139                        bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
140                                false, mWallpaper, null);
141                        saveSettingsLocked(mWallpaper);
142                    }
143                }
144            }
145        }
146    }
147
148    final Context mContext;
149    final IWindowManager mIWindowManager;
150    final IPackageManager mIPackageManager;
151    final MyPackageMonitor mMonitor;
152    WallpaperData mLastWallpaper;
153
154    SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
155
156    int mCurrentUserId;
157
158    static class WallpaperData {
159
160        int userId;
161
162        File wallpaperFile;
163
164        /**
165         * Client is currently writing a new image wallpaper.
166         */
167        boolean imageWallpaperPending;
168
169        /**
170         * Resource name if using a picture from the wallpaper gallery
171         */
172        String name = "";
173
174        /**
175         * The component name of the currently set live wallpaper.
176         */
177        ComponentName wallpaperComponent;
178
179        /**
180         * The component name of the wallpaper that should be set next.
181         */
182        ComponentName nextWallpaperComponent;
183
184        /**
185         * Name of the component used to display bitmap wallpapers from either the gallery or
186         * built-in wallpapers.
187         */
188        ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui",
189                "com.android.systemui.ImageWallpaper");
190
191        WallpaperConnection connection;
192        long lastDiedTime;
193        boolean wallpaperUpdating;
194        WallpaperObserver wallpaperObserver;
195
196        /**
197         * List of callbacks registered they should each be notified when the wallpaper is changed.
198         */
199        private RemoteCallbackList<IWallpaperManagerCallback> callbacks
200                = new RemoteCallbackList<IWallpaperManagerCallback>();
201
202        int width = -1;
203        int height = -1;
204
205        WallpaperData(int userId) {
206            this.userId = userId;
207            wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
208        }
209    }
210
211    class WallpaperConnection extends IWallpaperConnection.Stub
212            implements ServiceConnection {
213        final WallpaperInfo mInfo;
214        final Binder mToken = new Binder();
215        IWallpaperService mService;
216        IWallpaperEngine mEngine;
217        WallpaperData mWallpaper;
218        IRemoteCallback mReply;
219
220        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
221            mInfo = info;
222            mWallpaper = wallpaper;
223        }
224
225        @Override
226        public void onServiceConnected(ComponentName name, IBinder service) {
227            synchronized (mLock) {
228                if (mWallpaper.connection == this) {
229                    mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
230                    mService = IWallpaperService.Stub.asInterface(service);
231                    attachServiceLocked(this, mWallpaper);
232                    // XXX should probably do saveSettingsLocked() later
233                    // when we have an engine, but I'm not sure about
234                    // locking there and anyway we always need to be able to
235                    // recover if there is something wrong.
236                    saveSettingsLocked(mWallpaper);
237                }
238            }
239        }
240
241        @Override
242        public void onServiceDisconnected(ComponentName name) {
243            synchronized (mLock) {
244                mService = null;
245                mEngine = null;
246                if (mWallpaper.connection == this) {
247                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
248                    if (!mWallpaper.wallpaperUpdating
249                            && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME)
250                                > SystemClock.uptimeMillis()
251                            && mWallpaper.userId == mCurrentUserId) {
252                        Slog.w(TAG, "Reverting to built-in wallpaper!");
253                        clearWallpaperLocked(true, mWallpaper.userId, null);
254                    }
255                }
256            }
257        }
258
259        @Override
260        public void attachEngine(IWallpaperEngine engine) {
261            synchronized (mLock) {
262                mEngine = engine;
263            }
264        }
265
266        @Override
267        public void engineShown(IWallpaperEngine engine) {
268            synchronized (mLock) {
269                if (mReply != null) {
270                    long ident = Binder.clearCallingIdentity();
271                    try {
272                        mReply.sendResult(null);
273                    } catch (RemoteException e) {
274                        Binder.restoreCallingIdentity(ident);
275                    }
276                    mReply = null;
277                }
278            }
279        }
280
281        @Override
282        public ParcelFileDescriptor setWallpaper(String name) {
283            synchronized (mLock) {
284                if (mWallpaper.connection == this) {
285                    return updateWallpaperBitmapLocked(name, mWallpaper);
286                }
287                return null;
288            }
289        }
290    }
291
292    class MyPackageMonitor extends PackageMonitor {
293        @Override
294        public void onPackageUpdateFinished(String packageName, int uid) {
295            synchronized (mLock) {
296                for (int i = 0; i < mWallpaperMap.size(); i++) {
297                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
298                    if (wallpaper.wallpaperComponent != null
299                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
300                        wallpaper.wallpaperUpdating = false;
301                        ComponentName comp = wallpaper.wallpaperComponent;
302                        clearWallpaperComponentLocked(wallpaper);
303                        // Do this only for the current user's wallpaper
304                        if (wallpaper.userId == mCurrentUserId
305                                && !bindWallpaperComponentLocked(comp, false, false,
306                                        wallpaper, null)) {
307                            Slog.w(TAG, "Wallpaper no longer available; reverting to default");
308                            clearWallpaperLocked(false, wallpaper.userId, null);
309                        }
310                    }
311                }
312            }
313        }
314
315        @Override
316        public void onPackageModified(String packageName) {
317            synchronized (mLock) {
318                for (int i = 0; i < mWallpaperMap.size(); i++) {
319                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
320                    if (wallpaper.wallpaperComponent == null
321                            || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
322                        continue;
323                    }
324                    doPackagesChangedLocked(true, wallpaper);
325                }
326            }
327        }
328
329        @Override
330        public void onPackageUpdateStarted(String packageName, int uid) {
331            synchronized (mLock) {
332                for (int i = 0; i < mWallpaperMap.size(); i++) {
333                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
334                    if (wallpaper.wallpaperComponent != null
335                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
336                        wallpaper.wallpaperUpdating = true;
337                    }
338                }
339            }
340        }
341
342        @Override
343        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
344            synchronized (mLock) {
345                boolean changed = false;
346                for (int i = 0; i < mWallpaperMap.size(); i++) {
347                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
348                    boolean res = doPackagesChangedLocked(doit, wallpaper);
349                    changed |= res;
350                }
351                return changed;
352            }
353        }
354
355        @Override
356        public void onSomePackagesChanged() {
357            synchronized (mLock) {
358                for (int i = 0; i < mWallpaperMap.size(); i++) {
359                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
360                    doPackagesChangedLocked(true, wallpaper);
361                }
362            }
363        }
364
365        boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
366            boolean changed = false;
367            if (wallpaper.wallpaperComponent != null) {
368                int change = isPackageDisappearing(wallpaper.wallpaperComponent
369                        .getPackageName());
370                if (change == PACKAGE_PERMANENT_CHANGE
371                        || change == PACKAGE_TEMPORARY_CHANGE) {
372                    changed = true;
373                    if (doit) {
374                        Slog.w(TAG, "Wallpaper uninstalled, removing: "
375                                + wallpaper.wallpaperComponent);
376                        clearWallpaperLocked(false, wallpaper.userId, null);
377                    }
378                }
379            }
380            if (wallpaper.nextWallpaperComponent != null) {
381                int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
382                        .getPackageName());
383                if (change == PACKAGE_PERMANENT_CHANGE
384                        || change == PACKAGE_TEMPORARY_CHANGE) {
385                    wallpaper.nextWallpaperComponent = null;
386                }
387            }
388            if (wallpaper.wallpaperComponent != null
389                    && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
390                try {
391                    mContext.getPackageManager().getServiceInfo(
392                            wallpaper.wallpaperComponent, 0);
393                } catch (NameNotFoundException e) {
394                    Slog.w(TAG, "Wallpaper component gone, removing: "
395                            + wallpaper.wallpaperComponent);
396                    clearWallpaperLocked(false, wallpaper.userId, null);
397                }
398            }
399            if (wallpaper.nextWallpaperComponent != null
400                    && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
401                try {
402                    mContext.getPackageManager().getServiceInfo(
403                            wallpaper.nextWallpaperComponent, 0);
404                } catch (NameNotFoundException e) {
405                    wallpaper.nextWallpaperComponent = null;
406                }
407            }
408            return changed;
409        }
410    }
411
412    public WallpaperManagerService(Context context) {
413        if (DEBUG) Slog.v(TAG, "WallpaperService startup");
414        mContext = context;
415        mIWindowManager = IWindowManager.Stub.asInterface(
416                ServiceManager.getService(Context.WINDOW_SERVICE));
417        mIPackageManager = AppGlobals.getPackageManager();
418        mMonitor = new MyPackageMonitor();
419        mMonitor.register(context, null, true);
420        getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
421        loadSettingsLocked(UserHandle.USER_OWNER);
422    }
423
424    private static File getWallpaperDir(int userId) {
425        return Environment.getUserSystemDirectory(userId);
426    }
427
428    @Override
429    protected void finalize() throws Throwable {
430        super.finalize();
431        for (int i = 0; i < mWallpaperMap.size(); i++) {
432            WallpaperData wallpaper = mWallpaperMap.valueAt(i);
433            wallpaper.wallpaperObserver.stopWatching();
434        }
435    }
436
437    public void systemReady() {
438        if (DEBUG) Slog.v(TAG, "systemReady");
439        WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
440        switchWallpaper(wallpaper, null);
441        wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
442        wallpaper.wallpaperObserver.startWatching();
443
444        IntentFilter userFilter = new IntentFilter();
445        userFilter.addAction(Intent.ACTION_USER_REMOVED);
446        mContext.registerReceiver(new BroadcastReceiver() {
447            @Override
448            public void onReceive(Context context, Intent intent) {
449                String action = intent.getAction();
450                if (Intent.ACTION_USER_REMOVED.equals(action)) {
451                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
452                }
453            }
454        }, userFilter);
455        try {
456            ActivityManagerNative.getDefault().registerUserSwitchObserver(
457                    new IUserSwitchObserver.Stub() {
458                        @Override
459                        public void onUserSwitching(int newUserId, IRemoteCallback reply) {
460                            switchUser(newUserId, reply);
461                        }
462
463                        @Override
464                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
465                        }
466                    });
467        } catch (RemoteException e) {
468            // TODO Auto-generated catch block
469            e.printStackTrace();
470        }
471    }
472
473    String getName() {
474        synchronized (mLock) {
475            return mWallpaperMap.get(0).name;
476        }
477    }
478
479    void removeUser(int userId) {
480        synchronized (mLock) {
481            WallpaperData wallpaper = mWallpaperMap.get(userId);
482            if (wallpaper != null) {
483                wallpaper.wallpaperObserver.stopWatching();
484                mWallpaperMap.remove(userId);
485            }
486            File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
487            wallpaperFile.delete();
488            File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
489            wallpaperInfoFile.delete();
490        }
491    }
492
493    void switchUser(int userId, IRemoteCallback reply) {
494        synchronized (mLock) {
495            mCurrentUserId = userId;
496            WallpaperData wallpaper = mWallpaperMap.get(userId);
497            if (wallpaper == null) {
498                wallpaper = new WallpaperData(userId);
499                mWallpaperMap.put(userId, wallpaper);
500                loadSettingsLocked(userId);
501                wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
502                wallpaper.wallpaperObserver.startWatching();
503            }
504            switchWallpaper(wallpaper, reply);
505        }
506    }
507
508    void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
509        synchronized (mLock) {
510            RuntimeException e = null;
511            try {
512                ComponentName cname = wallpaper.wallpaperComponent != null ?
513                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
514                if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
515                    return;
516                }
517            } catch (RuntimeException e1) {
518                e = e1;
519            }
520            Slog.w(TAG, "Failure starting previous wallpaper", e);
521            clearWallpaperLocked(false, wallpaper.userId, reply);
522        }
523    }
524
525    public void clearWallpaper() {
526        if (DEBUG) Slog.v(TAG, "clearWallpaper");
527        synchronized (mLock) {
528            clearWallpaperLocked(false, UserHandle.getCallingUserId(), null);
529        }
530    }
531
532    void clearWallpaperLocked(boolean defaultFailed, int userId, IRemoteCallback reply) {
533        WallpaperData wallpaper = mWallpaperMap.get(userId);
534        File f = new File(getWallpaperDir(userId), WALLPAPER);
535        if (f.exists()) {
536            f.delete();
537        }
538        final long ident = Binder.clearCallingIdentity();
539        RuntimeException e = null;
540        try {
541            wallpaper.imageWallpaperPending = false;
542            if (userId != mCurrentUserId) return;
543            if (bindWallpaperComponentLocked(defaultFailed
544                    ? wallpaper.imageWallpaperComponent
545                    : null, true, false, wallpaper, reply)) {
546                return;
547            }
548        } catch (IllegalArgumentException e1) {
549            e = e1;
550        } finally {
551            Binder.restoreCallingIdentity(ident);
552        }
553
554        // This can happen if the default wallpaper component doesn't
555        // exist.  This should be a system configuration problem, but
556        // let's not let it crash the system and just live with no
557        // wallpaper.
558        Slog.e(TAG, "Default wallpaper component not found!", e);
559        clearWallpaperComponentLocked(wallpaper);
560        if (reply != null) {
561            try {
562                reply.sendResult(null);
563            } catch (RemoteException e1) {
564            }
565        }
566    }
567
568    public boolean hasNamedWallpaper(String name) {
569        synchronized (mLock) {
570            for (int i=0; i<mWallpaperMap.size(); i++) {
571                WallpaperData wd = mWallpaperMap.valueAt(i);
572                if (name.equals(wd.name)) {
573                    return true;
574                }
575            }
576        }
577        return false;
578    }
579
580    public void setDimensionHints(int width, int height) throws RemoteException {
581        checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
582        synchronized (mLock) {
583            int userId = UserHandle.getCallingUserId();
584            WallpaperData wallpaper = mWallpaperMap.get(userId);
585            if (wallpaper == null) {
586                throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
587            }
588            if (width <= 0 || height <= 0) {
589                throw new IllegalArgumentException("width and height must be > 0");
590            }
591
592            if (width != wallpaper.width || height != wallpaper.height) {
593                wallpaper.width = width;
594                wallpaper.height = height;
595                saveSettingsLocked(wallpaper);
596                if (mCurrentUserId != userId) return; // Don't change the properties now
597                if (wallpaper.connection != null) {
598                    if (wallpaper.connection.mEngine != null) {
599                        try {
600                            wallpaper.connection.mEngine.setDesiredSize(
601                                    width, height);
602                        } catch (RemoteException e) {
603                        }
604                        notifyCallbacksLocked(wallpaper);
605                    }
606                }
607            }
608        }
609    }
610
611    public int getWidthHint() throws RemoteException {
612        synchronized (mLock) {
613            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
614            return wallpaper.width;
615        }
616    }
617
618    public int getHeightHint() throws RemoteException {
619        synchronized (mLock) {
620            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
621            return wallpaper.height;
622        }
623    }
624
625    public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
626            Bundle outParams) {
627        synchronized (mLock) {
628            // This returns the current user's wallpaper, if called by a system service. Else it
629            // returns the wallpaper for the calling user.
630            int callingUid = Binder.getCallingUid();
631            int wallpaperUserId = 0;
632            if (callingUid == android.os.Process.SYSTEM_UID) {
633                wallpaperUserId = mCurrentUserId;
634            } else {
635                wallpaperUserId = UserHandle.getUserId(callingUid);
636            }
637            WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
638            try {
639                if (outParams != null) {
640                    outParams.putInt("width", wallpaper.width);
641                    outParams.putInt("height", wallpaper.height);
642                }
643                wallpaper.callbacks.register(cb);
644                File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
645                if (!f.exists()) {
646                    return null;
647                }
648                return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
649            } catch (FileNotFoundException e) {
650                /* Shouldn't happen as we check to see if the file exists */
651                Slog.w(TAG, "Error getting wallpaper", e);
652            }
653            return null;
654        }
655    }
656
657    public WallpaperInfo getWallpaperInfo() {
658        int userId = UserHandle.getCallingUserId();
659        synchronized (mLock) {
660            WallpaperData wallpaper = mWallpaperMap.get(userId);
661            if (wallpaper.connection != null) {
662                return wallpaper.connection.mInfo;
663            }
664            return null;
665        }
666    }
667
668    public ParcelFileDescriptor setWallpaper(String name) {
669        checkPermission(android.Manifest.permission.SET_WALLPAPER);
670        synchronized (mLock) {
671            if (DEBUG) Slog.v(TAG, "setWallpaper");
672            int userId = UserHandle.getCallingUserId();
673            WallpaperData wallpaper = mWallpaperMap.get(userId);
674            if (wallpaper == null) {
675                throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
676            }
677            final long ident = Binder.clearCallingIdentity();
678            try {
679                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
680                if (pfd != null) {
681                    wallpaper.imageWallpaperPending = true;
682                }
683                return pfd;
684            } finally {
685                Binder.restoreCallingIdentity(ident);
686            }
687        }
688    }
689
690    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
691        if (name == null) name = "";
692        try {
693            File dir = getWallpaperDir(wallpaper.userId);
694            if (!dir.exists()) {
695                dir.mkdir();
696                FileUtils.setPermissions(
697                        dir.getPath(),
698                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
699                        -1, -1);
700            }
701            File file = new File(dir, WALLPAPER);
702            ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
703                    MODE_CREATE|MODE_READ_WRITE);
704            if (!SELinux.restorecon(file)) {
705                return null;
706            }
707            wallpaper.name = name;
708            return fd;
709        } catch (FileNotFoundException e) {
710            Slog.w(TAG, "Error setting wallpaper", e);
711        }
712        return null;
713    }
714
715    public void setWallpaperComponent(ComponentName name) {
716        checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
717        synchronized (mLock) {
718            if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
719            int userId = UserHandle.getCallingUserId();
720            WallpaperData wallpaper = mWallpaperMap.get(userId);
721            if (wallpaper == null) {
722                throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
723            }
724            final long ident = Binder.clearCallingIdentity();
725            try {
726                wallpaper.imageWallpaperPending = false;
727                bindWallpaperComponentLocked(name, false, true, wallpaper, null);
728            } finally {
729                Binder.restoreCallingIdentity(ident);
730            }
731        }
732    }
733
734    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
735            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
736        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
737        // Has the component changed?
738        if (!force) {
739            if (wallpaper.connection != null) {
740                if (wallpaper.wallpaperComponent == null) {
741                    if (componentName == null) {
742                        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
743                        // Still using default wallpaper.
744                        return true;
745                    }
746                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
747                    // Changing to same wallpaper.
748                    if (DEBUG) Slog.v(TAG, "same wallpaper");
749                    return true;
750                }
751            }
752        }
753
754        try {
755            if (componentName == null) {
756                String defaultComponent =
757                    mContext.getString(com.android.internal.R.string.default_wallpaper_component);
758                if (defaultComponent != null) {
759                    // See if there is a default wallpaper component specified
760                    componentName = ComponentName.unflattenFromString(defaultComponent);
761                    if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName);
762                }
763                if (componentName == null) {
764                    // Fall back to static image wallpaper
765                    componentName = wallpaper.imageWallpaperComponent;
766                    //clearWallpaperComponentLocked();
767                    //return;
768                    if (DEBUG) Slog.v(TAG, "Using image wallpaper");
769                }
770            }
771            int serviceUserId = wallpaper.userId;
772            ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
773                    PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
774            if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
775                String msg = "Selected service does not require "
776                        + android.Manifest.permission.BIND_WALLPAPER
777                        + ": " + componentName;
778                if (fromUser) {
779                    throw new SecurityException(msg);
780                }
781                Slog.w(TAG, msg);
782                return false;
783            }
784
785            WallpaperInfo wi = null;
786
787            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
788            if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
789                // Make sure the selected service is actually a wallpaper service.
790                List<ResolveInfo> ris =
791                        mIPackageManager.queryIntentServices(intent,
792                                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
793                                PackageManager.GET_META_DATA, serviceUserId);
794                for (int i=0; i<ris.size(); i++) {
795                    ServiceInfo rsi = ris.get(i).serviceInfo;
796                    if (rsi.name.equals(si.name) &&
797                            rsi.packageName.equals(si.packageName)) {
798                        try {
799                            wi = new WallpaperInfo(mContext, ris.get(i));
800                        } catch (XmlPullParserException e) {
801                            if (fromUser) {
802                                throw new IllegalArgumentException(e);
803                            }
804                            Slog.w(TAG, e);
805                            return false;
806                        } catch (IOException e) {
807                            if (fromUser) {
808                                throw new IllegalArgumentException(e);
809                            }
810                            Slog.w(TAG, e);
811                            return false;
812                        }
813                        break;
814                    }
815                }
816                if (wi == null) {
817                    String msg = "Selected service is not a wallpaper: "
818                            + componentName;
819                    if (fromUser) {
820                        throw new SecurityException(msg);
821                    }
822                    Slog.w(TAG, msg);
823                    return false;
824                }
825            }
826
827            // Bind the service!
828            if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
829            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
830            intent.setComponent(componentName);
831            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
832                    com.android.internal.R.string.wallpaper_binding_label);
833            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
834                    mContext, 0,
835                    Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
836                            mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
837                    0, null, new UserHandle(serviceUserId)));
838            if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
839                String msg = "Unable to bind service: "
840                        + componentName;
841                if (fromUser) {
842                    throw new IllegalArgumentException(msg);
843                }
844                Slog.w(TAG, msg);
845                return false;
846            }
847            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
848                detachWallpaperLocked(mLastWallpaper);
849            }
850            wallpaper.wallpaperComponent = componentName;
851            wallpaper.connection = newConn;
852            wallpaper.lastDiedTime = SystemClock.uptimeMillis();
853            newConn.mReply = reply;
854            try {
855                if (wallpaper.userId == mCurrentUserId) {
856                    if (DEBUG)
857                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
858                    mIWindowManager.addWindowToken(newConn.mToken,
859                            WindowManager.LayoutParams.TYPE_WALLPAPER);
860                    mLastWallpaper = wallpaper;
861                }
862            } catch (RemoteException e) {
863            }
864        } catch (RemoteException e) {
865            String msg = "Remote exception for " + componentName + "\n" + e;
866            if (fromUser) {
867                throw new IllegalArgumentException(msg);
868            }
869            Slog.w(TAG, msg);
870            return false;
871        }
872        return true;
873    }
874
875    void detachWallpaperLocked(WallpaperData wallpaper) {
876        if (wallpaper.connection != null) {
877            if (wallpaper.connection.mReply != null) {
878                try {
879                    wallpaper.connection.mReply.sendResult(null);
880                } catch (RemoteException e) {
881                }
882                wallpaper.connection.mReply = null;
883            }
884            if (wallpaper.connection.mEngine != null) {
885                try {
886                    wallpaper.connection.mEngine.destroy();
887                } catch (RemoteException e) {
888                }
889            }
890            mContext.unbindService(wallpaper.connection);
891            try {
892                if (DEBUG)
893                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
894                mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
895            } catch (RemoteException e) {
896            }
897            wallpaper.connection.mService = null;
898            wallpaper.connection.mEngine = null;
899            wallpaper.connection = null;
900        }
901    }
902
903    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
904        wallpaper.wallpaperComponent = null;
905        detachWallpaperLocked(wallpaper);
906    }
907
908    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
909        try {
910            conn.mService.attach(conn, conn.mToken,
911                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
912                    wallpaper.width, wallpaper.height);
913        } catch (RemoteException e) {
914            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
915            if (!wallpaper.wallpaperUpdating) {
916                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
917            }
918        }
919    }
920
921    private void notifyCallbacksLocked(WallpaperData wallpaper) {
922        final int n = wallpaper.callbacks.beginBroadcast();
923        for (int i = 0; i < n; i++) {
924            try {
925                wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
926            } catch (RemoteException e) {
927
928                // The RemoteCallbackList will take care of removing
929                // the dead object for us.
930            }
931        }
932        wallpaper.callbacks.finishBroadcast();
933        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
934        mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
935    }
936
937    private void checkPermission(String permission) {
938        if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
939            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
940                    + ", must have permission " + permission);
941        }
942    }
943
944    private static JournaledFile makeJournaledFile(int userId) {
945        final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
946        return new JournaledFile(new File(base), new File(base + ".tmp"));
947    }
948
949    private void saveSettingsLocked(WallpaperData wallpaper) {
950        JournaledFile journal = makeJournaledFile(wallpaper.userId);
951        FileOutputStream stream = null;
952        try {
953            stream = new FileOutputStream(journal.chooseForWrite(), false);
954            XmlSerializer out = new FastXmlSerializer();
955            out.setOutput(stream, "utf-8");
956            out.startDocument(null, true);
957
958            out.startTag(null, "wp");
959            out.attribute(null, "width", Integer.toString(wallpaper.width));
960            out.attribute(null, "height", Integer.toString(wallpaper.height));
961            out.attribute(null, "name", wallpaper.name);
962            if (wallpaper.wallpaperComponent != null
963                    && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
964                out.attribute(null, "component",
965                        wallpaper.wallpaperComponent.flattenToShortString());
966            }
967            out.endTag(null, "wp");
968
969            out.endDocument();
970            stream.close();
971            journal.commit();
972        } catch (IOException e) {
973            try {
974                if (stream != null) {
975                    stream.close();
976                }
977            } catch (IOException ex) {
978                // Ignore
979            }
980            journal.rollback();
981        }
982    }
983
984    private void migrateFromOld() {
985        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
986        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
987        if (oldWallpaper.exists()) {
988            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
989            oldWallpaper.renameTo(newWallpaper);
990        }
991        if (oldInfo.exists()) {
992            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
993            oldInfo.renameTo(newInfo);
994        }
995    }
996
997    private void loadSettingsLocked(int userId) {
998        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
999
1000        JournaledFile journal = makeJournaledFile(userId);
1001        FileInputStream stream = null;
1002        File file = journal.chooseForRead();
1003        if (!file.exists()) {
1004            // This should only happen one time, when upgrading from a legacy system
1005            migrateFromOld();
1006        }
1007        WallpaperData wallpaper = mWallpaperMap.get(userId);
1008        if (wallpaper == null) {
1009            wallpaper = new WallpaperData(userId);
1010            mWallpaperMap.put(userId, wallpaper);
1011        }
1012        boolean success = false;
1013        try {
1014            stream = new FileInputStream(file);
1015            XmlPullParser parser = Xml.newPullParser();
1016            parser.setInput(stream, null);
1017
1018            int type;
1019            do {
1020                type = parser.next();
1021                if (type == XmlPullParser.START_TAG) {
1022                    String tag = parser.getName();
1023                    if ("wp".equals(tag)) {
1024                        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
1025                        wallpaper.height = Integer.parseInt(parser
1026                                .getAttributeValue(null, "height"));
1027                        wallpaper.name = parser.getAttributeValue(null, "name");
1028                        String comp = parser.getAttributeValue(null, "component");
1029                        wallpaper.nextWallpaperComponent = comp != null
1030                                ? ComponentName.unflattenFromString(comp)
1031                                : null;
1032                        if (wallpaper.nextWallpaperComponent == null
1033                                || "android".equals(wallpaper.nextWallpaperComponent
1034                                        .getPackageName())) {
1035                            wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent;
1036                        }
1037
1038                        if (DEBUG) {
1039                            Slog.v(TAG, "mWidth:" + wallpaper.width);
1040                            Slog.v(TAG, "mHeight:" + wallpaper.height);
1041                            Slog.v(TAG, "mName:" + wallpaper.name);
1042                            Slog.v(TAG, "mNextWallpaperComponent:"
1043                                    + wallpaper.nextWallpaperComponent);
1044                        }
1045                    }
1046                }
1047            } while (type != XmlPullParser.END_DOCUMENT);
1048            success = true;
1049        } catch (NullPointerException e) {
1050            Slog.w(TAG, "failed parsing " + file + " " + e);
1051        } catch (NumberFormatException e) {
1052            Slog.w(TAG, "failed parsing " + file + " " + e);
1053        } catch (XmlPullParserException e) {
1054            Slog.w(TAG, "failed parsing " + file + " " + e);
1055        } catch (IOException e) {
1056            Slog.w(TAG, "failed parsing " + file + " " + e);
1057        } catch (IndexOutOfBoundsException e) {
1058            Slog.w(TAG, "failed parsing " + file + " " + e);
1059        }
1060        try {
1061            if (stream != null) {
1062                stream.close();
1063            }
1064        } catch (IOException e) {
1065            // Ignore
1066        }
1067
1068        if (!success) {
1069            wallpaper.width = -1;
1070            wallpaper.height = -1;
1071            wallpaper.name = "";
1072        }
1073
1074        // We always want to have some reasonable width hint.
1075        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
1076        Display d = wm.getDefaultDisplay();
1077        int baseSize = d.getMaximumSizeDimension();
1078        if (wallpaper.width < baseSize) {
1079            wallpaper.width = baseSize;
1080        }
1081        if (wallpaper.height < baseSize) {
1082            wallpaper.height = baseSize;
1083        }
1084    }
1085
1086    // Called by SystemBackupAgent after files are restored to disk.
1087    void settingsRestored() {
1088        // TODO: If necessary, make it work for secondary users as well. This currently assumes
1089        // restores only to the primary user
1090        if (DEBUG) Slog.v(TAG, "settingsRestored");
1091        WallpaperData wallpaper = null;
1092        boolean success = false;
1093        synchronized (mLock) {
1094            loadSettingsLocked(0);
1095            wallpaper = mWallpaperMap.get(0);
1096            if (wallpaper.nextWallpaperComponent != null
1097                    && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
1098                if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
1099                        wallpaper, null)) {
1100                    // No such live wallpaper or other failure; fall back to the default
1101                    // live wallpaper (since the profile being restored indicated that the
1102                    // user had selected a live rather than static one).
1103                    bindWallpaperComponentLocked(null, false, false, wallpaper, null);
1104                }
1105                success = true;
1106            } else {
1107                // If there's a wallpaper name, we use that.  If that can't be loaded, then we
1108                // use the default.
1109                if ("".equals(wallpaper.name)) {
1110                    if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
1111                    success = true;
1112                } else {
1113                    if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
1114                    success = restoreNamedResourceLocked(wallpaper);
1115                }
1116                if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
1117                if (success) {
1118                    bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
1119                            wallpaper, null);
1120                }
1121            }
1122        }
1123
1124        if (!success) {
1125            Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
1126            wallpaper.name = "";
1127            getWallpaperDir(0).delete();
1128        }
1129
1130        synchronized (mLock) {
1131            saveSettingsLocked(wallpaper);
1132        }
1133    }
1134
1135    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
1136        if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
1137            String resName = wallpaper.name.substring(4);
1138
1139            String pkg = null;
1140            int colon = resName.indexOf(':');
1141            if (colon > 0) {
1142                pkg = resName.substring(0, colon);
1143            }
1144
1145            String ident = null;
1146            int slash = resName.lastIndexOf('/');
1147            if (slash > 0) {
1148                ident = resName.substring(slash+1);
1149            }
1150
1151            String type = null;
1152            if (colon > 0 && slash > 0 && (slash-colon) > 1) {
1153                type = resName.substring(colon+1, slash);
1154            }
1155
1156            if (pkg != null && ident != null && type != null) {
1157                int resId = -1;
1158                InputStream res = null;
1159                FileOutputStream fos = null;
1160                try {
1161                    Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
1162                    Resources r = c.getResources();
1163                    resId = r.getIdentifier(resName, null, null);
1164                    if (resId == 0) {
1165                        Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
1166                                + " ident=" + ident);
1167                        return false;
1168                    }
1169
1170                    res = r.openRawResource(resId);
1171                    if (wallpaper.wallpaperFile.exists()) {
1172                        wallpaper.wallpaperFile.delete();
1173                    }
1174                    fos = new FileOutputStream(wallpaper.wallpaperFile);
1175
1176                    byte[] buffer = new byte[32768];
1177                    int amt;
1178                    while ((amt=res.read(buffer)) > 0) {
1179                        fos.write(buffer, 0, amt);
1180                    }
1181                    // mWallpaperObserver will notice the close and send the change broadcast
1182
1183                    Slog.v(TAG, "Restored wallpaper: " + resName);
1184                    return true;
1185                } catch (NameNotFoundException e) {
1186                    Slog.e(TAG, "Package name " + pkg + " not found");
1187                } catch (Resources.NotFoundException e) {
1188                    Slog.e(TAG, "Resource not found: " + resId);
1189                } catch (IOException e) {
1190                    Slog.e(TAG, "IOException while restoring wallpaper ", e);
1191                } finally {
1192                    if (res != null) {
1193                        try {
1194                            res.close();
1195                        } catch (IOException ex) {}
1196                    }
1197                    if (fos != null) {
1198                        FileUtils.sync(fos);
1199                        try {
1200                            fos.close();
1201                        } catch (IOException ex) {}
1202                    }
1203                }
1204            }
1205        }
1206        return false;
1207    }
1208
1209    @Override
1210    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1211        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1212                != PackageManager.PERMISSION_GRANTED) {
1213
1214            pw.println("Permission Denial: can't dump wallpaper service from from pid="
1215                    + Binder.getCallingPid()
1216                    + ", uid=" + Binder.getCallingUid());
1217            return;
1218        }
1219
1220        synchronized (mLock) {
1221            pw.println("Current Wallpaper Service state:");
1222            for (int i = 0; i < mWallpaperMap.size(); i++) {
1223                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1224                pw.println(" User " + wallpaper.userId + ":");
1225                pw.print("  mWidth=");
1226                pw.print(wallpaper.width);
1227                pw.print(" mHeight=");
1228                pw.println(wallpaper.height);
1229                pw.print("  mName=");
1230                pw.println(wallpaper.name);
1231                pw.print("  mWallpaperComponent=");
1232                pw.println(wallpaper.wallpaperComponent);
1233                if (wallpaper.connection != null) {
1234                    WallpaperConnection conn = wallpaper.connection;
1235                    pw.print("  Wallpaper connection ");
1236                    pw.print(conn);
1237                    pw.println(":");
1238                    if (conn.mInfo != null) {
1239                        pw.print("    mInfo.component=");
1240                        pw.println(conn.mInfo.getComponent());
1241                    }
1242                    pw.print("    mToken=");
1243                    pw.println(conn.mToken);
1244                    pw.print("    mService=");
1245                    pw.println(conn.mService);
1246                    pw.print("    mEngine=");
1247                    pw.println(conn.mEngine);
1248                    pw.print("    mLastDiedTime=");
1249                    pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
1250                }
1251            }
1252        }
1253    }
1254}
1255