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