1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wallpaper;
18 
19 import static android.app.WallpaperManager.FLAG_LOCK;
20 import static android.app.WallpaperManager.FLAG_SYSTEM;
21 import static android.view.Display.DEFAULT_DISPLAY;
22 
23 import static com.android.server.wallpaper.WallpaperDisplayHelper.DisplayData;
24 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
25 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP;
26 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_INFO;
27 import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
28 import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
29 
30 import android.annotation.Nullable;
31 import android.app.WallpaperColors;
32 import android.app.WallpaperManager;
33 import android.app.WallpaperManager.SetWallpaperFlags;
34 import android.app.backup.WallpaperBackupHelper;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.pm.PackageManager;
38 import android.content.res.Resources;
39 import android.graphics.Color;
40 import android.os.FileUtils;
41 import android.os.SystemProperties;
42 import android.util.Slog;
43 import android.util.SparseArray;
44 import android.util.Xml;
45 
46 import com.android.internal.R;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.util.JournaledFile;
49 import com.android.modules.utils.TypedXmlPullParser;
50 import com.android.modules.utils.TypedXmlSerializer;
51 
52 import libcore.io.IoUtils;
53 
54 import org.xmlpull.v1.XmlPullParser;
55 import org.xmlpull.v1.XmlPullParserException;
56 
57 import java.io.File;
58 import java.io.FileInputStream;
59 import java.io.FileNotFoundException;
60 import java.io.FileOutputStream;
61 import java.io.IOException;
62 import java.io.InputStream;
63 import java.util.HashMap;
64 import java.util.Map;
65 
66 /**
67  * Helper for the wallpaper loading / saving / xml parsing
68  * Only meant to be used lock held by WallpaperManagerService
69  * Only meant to be instantiated once by WallpaperManagerService
70  */
71 class WallpaperDataParser {
72 
73     private static final String TAG = WallpaperDataParser.class.getSimpleName();
74     private static final boolean DEBUG = false;
75     private final ComponentName mImageWallpaper;
76     private final WallpaperDisplayHelper mWallpaperDisplayHelper;
77     private final WallpaperCropper mWallpaperCropper;
78     private final Context mContext;
79 
80     private final boolean mIsLockscreenLiveWallpaperEnabled;
81 
WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper, WallpaperCropper wallpaperCropper)82     WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper,
83             WallpaperCropper wallpaperCropper) {
84         mContext = context;
85         mWallpaperDisplayHelper = wallpaperDisplayHelper;
86         mWallpaperCropper = wallpaperCropper;
87         mImageWallpaper = ComponentName.unflattenFromString(
88                 context.getResources().getString(R.string.image_wallpaper_component));
89         mIsLockscreenLiveWallpaperEnabled =
90                 SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true);
91     }
92 
makeJournaledFile(int userId)93     private JournaledFile makeJournaledFile(int userId) {
94         final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
95         return new JournaledFile(new File(base), new File(base + ".tmp"));
96     }
97 
98     static class WallpaperLoadingResult {
99 
100         private final WallpaperData mSystemWallpaperData;
101 
102         @Nullable
103         private final WallpaperData mLockWallpaperData;
104 
105         private final boolean mSuccess;
106 
WallpaperLoadingResult( WallpaperData systemWallpaperData, WallpaperData lockWallpaperData, boolean success)107         private WallpaperLoadingResult(
108                 WallpaperData systemWallpaperData,
109                 WallpaperData lockWallpaperData,
110                 boolean success) {
111             mSystemWallpaperData = systemWallpaperData;
112             mLockWallpaperData = lockWallpaperData;
113             mSuccess = success;
114         }
115 
getSystemWallpaperData()116         public WallpaperData getSystemWallpaperData() {
117             return mSystemWallpaperData;
118         }
119 
getLockWallpaperData()120         public WallpaperData getLockWallpaperData() {
121             return mLockWallpaperData;
122         }
123 
success()124         public boolean success() {
125             return mSuccess;
126         }
127     }
128 
129     /**
130      * TODO(b/197814683) adapt comment once flag is removed
131      *
132      * Load the system wallpaper (and the lock wallpaper, if it exists) from disk
133      * @param userId the id of the user for which the wallpaper should be loaded
134      * @param keepDimensionHints if false, parse and set the
135      *                      {@link DisplayData} width and height for the specified userId
136      * @param wallpaper the wallpaper object to reuse to do the modifications.
137      *                      If null, a new object will be created.
138      * @param lockWallpaper the lock wallpaper object to reuse to do the modifications.
139      *                      If null, a new object will be created.
140      * @param which The wallpaper(s) to load. Only has effect if
141      *                      {@link WallpaperManager#isLockscreenLiveWallpaperEnabled} is true,
142      *                      otherwise both wallpaper will always be loaded.
143      * @return a {@link WallpaperLoadingResult} object containing the wallpaper data.
144      *                      This object will contain the {@code wallpaper} and
145      *                      {@code lockWallpaper} provided as parameters, if they are not null.
146      */
loadSettingsLocked(int userId, boolean keepDimensionHints, WallpaperData wallpaper, WallpaperData lockWallpaper, @SetWallpaperFlags int which)147     public WallpaperLoadingResult loadSettingsLocked(int userId, boolean keepDimensionHints,
148             WallpaperData wallpaper, WallpaperData lockWallpaper, @SetWallpaperFlags int which) {
149         JournaledFile journal = makeJournaledFile(userId);
150         FileInputStream stream = null;
151         File file = journal.chooseForRead();
152 
153         boolean migrateFromOld = wallpaper == null;
154 
155         boolean separateLockscreenEngine = mIsLockscreenLiveWallpaperEnabled;
156         boolean loadSystem = !separateLockscreenEngine || (which & FLAG_SYSTEM) != 0;
157         boolean loadLock = !separateLockscreenEngine || (which & FLAG_LOCK) != 0;
158 
159         // don't reuse the wallpaper objects in the new version
160         if (separateLockscreenEngine) {
161             wallpaper = null;
162             lockWallpaper = null;
163         }
164 
165         if (wallpaper == null && loadSystem) {
166             // Do this once per boot
167             if (migrateFromOld) migrateFromOld();
168             wallpaper = new WallpaperData(userId, FLAG_SYSTEM);
169             wallpaper.allowBackup = true;
170             if (!wallpaper.cropExists()) {
171                 if (wallpaper.sourceExists()) {
172                     mWallpaperCropper.generateCrop(wallpaper);
173                 } else {
174                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
175                 }
176             }
177         }
178 
179         final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
180         boolean success = false;
181 
182         try {
183             stream = new FileInputStream(file);
184             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
185 
186             int type;
187             do {
188                 type = parser.next();
189                 if (type == XmlPullParser.START_TAG) {
190                     String tag = parser.getName();
191                     if (("wp".equals(tag) && loadSystem)
192                             || ("kwp".equals(tag) && mIsLockscreenLiveWallpaperEnabled
193                                 && loadLock)) {
194 
195                         if ("kwp".equals(tag) && lockWallpaper == null) {
196                             lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
197                         }
198                         WallpaperData wallpaperToParse =
199                                 "wp".equals(tag) ? wallpaper : lockWallpaper;
200 
201                         parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
202 
203                         String comp = parser.getAttributeValue(null, "component");
204                         wallpaperToParse.nextWallpaperComponent = comp != null
205                                 ? ComponentName.unflattenFromString(comp)
206                                 : null;
207                         if (wallpaperToParse.nextWallpaperComponent == null
208                                 || "android".equals(wallpaperToParse.nextWallpaperComponent
209                                 .getPackageName())) {
210                             wallpaperToParse.nextWallpaperComponent = mImageWallpaper;
211                         }
212 
213                         if (DEBUG) {
214                             Slog.v(TAG, "mWidth:" + wpdData.mWidth);
215                             Slog.v(TAG, "mHeight:" + wpdData.mHeight);
216                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
217                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
218                             Slog.v(TAG, "mName:" + wallpaper.name);
219                             Slog.v(TAG, "mNextWallpaperComponent:"
220                                     + wallpaper.nextWallpaperComponent);
221                         }
222                     } else if ("kwp".equals(tag) && !mIsLockscreenLiveWallpaperEnabled) {
223                         // keyguard-specific wallpaper for this user (legacy code)
224                         if (lockWallpaper == null) {
225                             lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
226                         }
227                         parseWallpaperAttributes(parser, lockWallpaper, false);
228                     }
229                 }
230             } while (type != XmlPullParser.END_DOCUMENT);
231             success = true;
232         } catch (FileNotFoundException e) {
233             Slog.w(TAG, "no current wallpaper -- first boot?");
234         } catch (NullPointerException e) {
235             Slog.w(TAG, "failed parsing " + file + " " + e);
236         } catch (NumberFormatException e) {
237             Slog.w(TAG, "failed parsing " + file + " " + e);
238         } catch (XmlPullParserException e) {
239             Slog.w(TAG, "failed parsing " + file + " " + e);
240         } catch (IOException e) {
241             Slog.w(TAG, "failed parsing " + file + " " + e);
242         } catch (IndexOutOfBoundsException e) {
243             Slog.w(TAG, "failed parsing " + file + " " + e);
244         }
245         IoUtils.closeQuietly(stream);
246 
247         mWallpaperDisplayHelper.ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
248 
249         if (loadSystem) {
250             if (!success) {
251                 wallpaper.cropHint.set(0, 0, 0, 0);
252                 wpdData.mPadding.set(0, 0, 0, 0);
253                 wallpaper.name = "";
254             } else {
255                 if (wallpaper.wallpaperId <= 0) {
256                     wallpaper.wallpaperId = makeWallpaperIdLocked();
257                     if (DEBUG) {
258                         Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
259                                 + "); now " + wallpaper.wallpaperId);
260                     }
261                 }
262             }
263             ensureSaneWallpaperData(wallpaper);
264             wallpaper.mWhich = lockWallpaper != null ? FLAG_SYSTEM : FLAG_SYSTEM | FLAG_LOCK;
265         }
266 
267         if (loadLock) {
268             if (!success) lockWallpaper = null;
269             if (lockWallpaper != null) {
270                 ensureSaneWallpaperData(lockWallpaper);
271                 lockWallpaper.mWhich = FLAG_LOCK;
272             }
273         }
274 
275         return new WallpaperLoadingResult(wallpaper, lockWallpaper, success);
276     }
277 
ensureSaneWallpaperData(WallpaperData wallpaper)278     private void ensureSaneWallpaperData(WallpaperData wallpaper) {
279         // Only overwrite cropHint if the rectangle is invalid.
280         if (wallpaper.cropHint.width() < 0
281                 || wallpaper.cropHint.height() < 0) {
282             wallpaper.cropHint.set(0, 0, 0, 0);
283         }
284     }
285 
286 
migrateFromOld()287     private void migrateFromOld() {
288         // Pre-N, what existed is the one we're now using as the display crop
289         File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
290         // In the very-long-ago, imagery lived with the settings app
291         File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
292         File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
293 
294         // Migrations from earlier wallpaper image storage schemas
295         if (preNWallpaper.exists()) {
296             if (!newWallpaper.exists()) {
297                 // we've got the 'wallpaper' crop file but not the nominal source image,
298                 // so do the simple "just take everything" straight copy of legacy data
299                 if (DEBUG) {
300                     Slog.i(TAG, "Migrating wallpaper schema");
301                 }
302                 FileUtils.copyFile(preNWallpaper, newWallpaper);
303             } // else we're in the usual modern case: both source & crop exist
304         } else if (originalWallpaper.exists()) {
305             // VERY old schema; make sure things exist and are in the right place
306             if (DEBUG) {
307                 Slog.i(TAG, "Migrating antique wallpaper schema");
308             }
309             File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
310             if (oldInfo.exists()) {
311                 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
312                 oldInfo.renameTo(newInfo);
313             }
314 
315             FileUtils.copyFile(originalWallpaper, preNWallpaper);
316             originalWallpaper.renameTo(newWallpaper);
317         }
318     }
319 
320     @VisibleForTesting
parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)321     void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
322             boolean keepDimensionHints) throws XmlPullParserException {
323         final int id = parser.getAttributeInt(null, "id", -1);
324         if (id != -1) {
325             wallpaper.wallpaperId = id;
326             if (id > WallpaperUtils.getCurrentWallpaperId()) {
327                 WallpaperUtils.setCurrentWallpaperId(id);
328             }
329         } else {
330             wallpaper.wallpaperId = makeWallpaperIdLocked();
331         }
332 
333         final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
334 
335         if (!keepDimensionHints) {
336             wpData.mWidth = parser.getAttributeInt(null, "width");
337             wpData.mHeight = parser.getAttributeInt(null, "height");
338         }
339         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
340         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
341         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
342         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
343         wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
344         wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
345         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
346         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
347         wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
348         int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
349         if (dimAmountsCount > 0) {
350             SparseArray<Float> allDimAmounts = new SparseArray<>(dimAmountsCount);
351             for (int i = 0; i < dimAmountsCount; i++) {
352                 int uid = getAttributeInt(parser, "dimUID" + i, 0);
353                 float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
354                 allDimAmounts.put(uid, dimValue);
355             }
356             wallpaper.mUidToDimAmount = allDimAmounts;
357         }
358         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
359         int allColorsCount =  getAttributeInt(parser, "allColorsCount", 0);
360         if (allColorsCount > 0) {
361             Map<Integer, Integer> allColors = new HashMap<>(allColorsCount);
362             for (int i = 0; i < allColorsCount; i++) {
363                 int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0);
364                 int population = getAttributeInt(parser, "allColorsPopulation" + i, 0);
365                 allColors.put(colorInt, population);
366             }
367             int colorHints = getAttributeInt(parser, "colorHints", 0);
368             wallpaper.primaryColors = new WallpaperColors(allColors, colorHints);
369         } else if (colorsCount > 0) {
370             Color primary = null, secondary = null, tertiary = null;
371             for (int i = 0; i < colorsCount; i++) {
372                 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
373                 if (i == 0) {
374                     primary = color;
375                 } else if (i == 1) {
376                     secondary = color;
377                 } else if (i == 2) {
378                     tertiary = color;
379                 } else {
380                     break;
381                 }
382             }
383             int colorHints = getAttributeInt(parser, "colorHints", 0);
384             wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
385         }
386         wallpaper.name = parser.getAttributeValue(null, "name");
387         wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false);
388     }
389 
getAttributeInt(TypedXmlPullParser parser, String name, int defValue)390     private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
391         return parser.getAttributeInt(null, name, defValue);
392     }
393 
getAttributeFloat(TypedXmlPullParser parser, String name, float defValue)394     private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
395         return parser.getAttributeFloat(null, name, defValue);
396     }
397 
saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper)398     void saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper) {
399         JournaledFile journal = makeJournaledFile(userId);
400         FileOutputStream fstream = null;
401         try {
402             fstream = new FileOutputStream(journal.chooseForWrite(), false);
403             TypedXmlSerializer out = Xml.resolveSerializer(fstream);
404             out.startDocument(null, true);
405 
406             if (wallpaper != null) {
407                 writeWallpaperAttributes(out, "wp", wallpaper);
408             }
409 
410             if (lockWallpaper != null) {
411                 writeWallpaperAttributes(out, "kwp", lockWallpaper);
412             }
413 
414             out.endDocument();
415 
416             fstream.flush();
417             FileUtils.sync(fstream);
418             fstream.close();
419             journal.commit();
420         } catch (IOException e) {
421             IoUtils.closeQuietly(fstream);
422             journal.rollback();
423         }
424     }
425 
426     @VisibleForTesting
writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper)427     void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper)
428             throws IllegalArgumentException, IllegalStateException, IOException {
429         if (DEBUG) {
430             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
431         }
432         final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
433         out.startTag(null, tag);
434         out.attributeInt(null, "id", wallpaper.wallpaperId);
435         out.attributeInt(null, "width", wpdData.mWidth);
436         out.attributeInt(null, "height", wpdData.mHeight);
437 
438         out.attributeInt(null, "cropLeft", wallpaper.cropHint.left);
439         out.attributeInt(null, "cropTop", wallpaper.cropHint.top);
440         out.attributeInt(null, "cropRight", wallpaper.cropHint.right);
441         out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom);
442 
443         if (wpdData.mPadding.left != 0) {
444             out.attributeInt(null, "paddingLeft", wpdData.mPadding.left);
445         }
446         if (wpdData.mPadding.top != 0) {
447             out.attributeInt(null, "paddingTop", wpdData.mPadding.top);
448         }
449         if (wpdData.mPadding.right != 0) {
450             out.attributeInt(null, "paddingRight", wpdData.mPadding.right);
451         }
452         if (wpdData.mPadding.bottom != 0) {
453             out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
454         }
455 
456         out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
457         int dimAmountsCount = wallpaper.mUidToDimAmount.size();
458         out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
459         if (dimAmountsCount > 0) {
460             int index = 0;
461             for (int i = 0; i < wallpaper.mUidToDimAmount.size(); i++) {
462                 out.attributeInt(null, "dimUID" + index, wallpaper.mUidToDimAmount.keyAt(i));
463                 out.attributeFloat(null, "dimValue" + index, wallpaper.mUidToDimAmount.valueAt(i));
464                 index++;
465             }
466         }
467 
468         if (wallpaper.primaryColors != null) {
469             int colorsCount = wallpaper.primaryColors.getMainColors().size();
470             out.attributeInt(null, "colorsCount", colorsCount);
471             if (colorsCount > 0) {
472                 for (int i = 0; i < colorsCount; i++) {
473                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
474                     out.attributeInt(null, "colorValue" + i, wc.toArgb());
475                 }
476             }
477 
478             int allColorsCount = wallpaper.primaryColors.getAllColors().size();
479             out.attributeInt(null, "allColorsCount", allColorsCount);
480             if (allColorsCount > 0) {
481                 int index = 0;
482                 for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors()
483                         .entrySet()) {
484                     out.attributeInt(null, "allColorsValue" + index, entry.getKey());
485                     out.attributeInt(null, "allColorsPopulation" + index, entry.getValue());
486                     index++;
487                 }
488             }
489 
490             out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
491         }
492 
493         out.attribute(null, "name", wallpaper.name);
494         if (wallpaper.wallpaperComponent != null
495                 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
496             out.attribute(null, "component",
497                     wallpaper.wallpaperComponent.flattenToShortString());
498         }
499 
500         if (wallpaper.allowBackup) {
501             out.attributeBoolean(null, "backup", true);
502         }
503 
504         out.endTag(null, tag);
505     }
506 
507     // Restore the named resource bitmap to both source + crop files
restoreNamedResourceLocked(WallpaperData wallpaper)508     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
509         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
510             String resName = wallpaper.name.substring(4);
511 
512             String pkg = null;
513             int colon = resName.indexOf(':');
514             if (colon > 0) {
515                 pkg = resName.substring(0, colon);
516             }
517 
518             String ident = null;
519             int slash = resName.lastIndexOf('/');
520             if (slash > 0) {
521                 ident = resName.substring(slash + 1);
522             }
523 
524             String type = null;
525             if (colon > 0 && slash > 0 && (slash - colon) > 1) {
526                 type = resName.substring(colon + 1, slash);
527             }
528 
529             if (pkg != null && ident != null && type != null) {
530                 int resId = -1;
531                 InputStream res = null;
532                 FileOutputStream fos = null;
533                 FileOutputStream cos = null;
534                 try {
535                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
536                     Resources r = c.getResources();
537                     resId = r.getIdentifier(resName, null, null);
538                     if (resId == 0) {
539                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
540                                 + " ident=" + ident);
541                         return false;
542                     }
543 
544                     res = r.openRawResource(resId);
545                     if (wallpaper.getWallpaperFile().exists()) {
546                         wallpaper.getWallpaperFile().delete();
547                         wallpaper.getCropFile().delete();
548                     }
549                     fos = new FileOutputStream(wallpaper.getWallpaperFile());
550                     cos = new FileOutputStream(wallpaper.getCropFile());
551 
552                     byte[] buffer = new byte[32768];
553                     int amt;
554                     while ((amt = res.read(buffer)) > 0) {
555                         fos.write(buffer, 0, amt);
556                         cos.write(buffer, 0, amt);
557                     }
558                     // mWallpaperObserver will notice the close and send the change broadcast
559 
560                     Slog.v(TAG, "Restored wallpaper: " + resName);
561                     return true;
562                 } catch (PackageManager.NameNotFoundException e) {
563                     Slog.e(TAG, "Package name " + pkg + " not found");
564                 } catch (Resources.NotFoundException e) {
565                     Slog.e(TAG, "Resource not found: " + resId);
566                 } catch (IOException e) {
567                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
568                 } finally {
569                     IoUtils.closeQuietly(res);
570                     if (fos != null) {
571                         FileUtils.sync(fos);
572                     }
573                     if (cos != null) {
574                         FileUtils.sync(cos);
575                     }
576                     IoUtils.closeQuietly(fos);
577                     IoUtils.closeQuietly(cos);
578                 }
579             }
580         }
581         return false;
582     }
583 }
584