diff options
author | Joe Onorato <joeo@android.com> | 2009-07-28 18:24:51 -0700 |
---|---|---|
committer | Joe Onorato <joeo@android.com> | 2009-07-29 12:05:36 -0700 |
commit | 9bb8fd77c8dc177aab9ac96bed4f55972dcda70a (patch) | |
tree | 65f29284d166ac676ef052df99ed8bcfb237ccbb /services | |
parent | 0ac031b3d29c6de90895c875991585812dc7388f (diff) | |
download | frameworks_base-9bb8fd77c8dc177aab9ac96bed4f55972dcda70a.zip frameworks_base-9bb8fd77c8dc177aab9ac96bed4f55972dcda70a.tar.gz frameworks_base-9bb8fd77c8dc177aab9ac96bed4f55972dcda70a.tar.bz2 |
Only restore the bits for wallpapers that aren't built in.
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/JournaledFile.java | 107 | ||||
-rw-r--r-- | services/java/com/android/server/SystemBackupAgent.java | 67 | ||||
-rw-r--r-- | services/java/com/android/server/WallpaperService.java | 328 |
3 files changed, 431 insertions, 71 deletions
diff --git a/services/java/com/android/server/JournaledFile.java b/services/java/com/android/server/JournaledFile.java new file mode 100644 index 0000000..3d1f52d --- /dev/null +++ b/services/java/com/android/server/JournaledFile.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import java.io.File; +import java.io.IOException; + +public class JournaledFile { + File mReal; + File mTemp; + boolean mWriting; + + public JournaledFile(File real, File temp) { + mReal = real; + mTemp = temp; + } + + /** Returns the file for you to read. + * @more + * Prefers the real file. If it doesn't exist, uses the temp one, and then copies + * it to the real one. If there is both a real file and a temp one, assumes that the + * temp one isn't fully written and deletes it. + */ + public File chooseForRead() { + File result; + if (mReal.exists()) { + result = mReal; + if (mTemp.exists()) { + mTemp.delete(); + } + } else if (mTemp.exists()) { + result = mTemp; + mTemp.renameTo(mReal); + } else { + return mReal; + } + return result; + } + + /** + * Returns a file for you to write. + * @more + * If a write is already happening, throws. In other words, you must provide your + * own locking. + * <p> + * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes. + */ + public File chooseForWrite() { + if (mWriting) { + throw new IllegalStateException("uncommitted write already in progress"); + } + if (!mReal.exists()) { + // If the real one doesn't exist, it's either because this is the first time + // or because something went wrong while copying them. In this case, we can't + // trust anything that's in temp. In order to have the chooseForRead code not + // use the temporary one until it's fully written, create an empty file + // for real, which will we'll shortly delete. + try { + mReal.createNewFile(); + } catch (IOException e) { + // Ignore + } + } + + if (mTemp.exists()) { + mTemp.delete(); + } + mWriting = true; + return mTemp; + } + + /** + * Commit changes. + */ + public void commit() { + if (!mWriting) { + throw new IllegalStateException("no file to commit"); + } + mWriting = false; + mTemp.renameTo(mReal); + } + + /** + * Roll back changes. + */ + public void rollback() { + if (!mWriting) { + throw new IllegalStateException("no file to roll back"); + } + mWriting = false; + mTemp.delete(); + } +} diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java new file mode 100644 index 0000000..b681961 --- /dev/null +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.backup.AbsoluteFileBackupHelper; +import android.backup.BackupDataInput; +import android.backup.BackupDataInputStream; +import android.backup.BackupDataOutput; +import android.backup.BackupHelper; +import android.backup.BackupHelperAgent; +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; +import android.os.SystemService; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +/** + * Backup agent for various system-managed data + */ +public class SystemBackupAgent extends BackupHelperAgent { + private static final String TAG = "SystemBackupAgent"; + + private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper"; + private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml"; + + @Override + public void onCreate() { + addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this, + new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO })); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + boolean success = false; + try { + super.onRestore(data, appVersionCode, newState); + + WallpaperService wallpaper = (WallpaperService)ServiceManager.getService( + Context.WALLPAPER_SERVICE); + wallpaper.settingsRestored(); + } catch (IOException ex) { + // If there was a failure, delete everything for the wallpaper, this is too aggresive, + // but this is hopefully a rare failure. + Log.d(TAG, "restore failed", ex); + (new File(WALLPAPER_IMAGE)).delete(); + (new File(WALLPAPER_INFO)).delete(); + } + } +} diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java index d921baf..11981df 100644 --- a/services/java/com/android/server/WallpaperService.java +++ b/services/java/com/android/server/WallpaperService.java @@ -24,8 +24,9 @@ import android.app.IWallpaperServiceCallback; import android.backup.BackupManager; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; import android.os.Binder; import android.os.RemoteException; import android.os.FileObserver; @@ -33,30 +34,38 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteCallbackList; import android.util.Config; import android.util.Log; +import android.util.Xml; +import java.io.IOException; +import java.io.InputStream; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import com.android.internal.util.FastXmlSerializer; class WallpaperService extends IWallpaperService.Stub { - private static final String TAG = WallpaperService.class.getSimpleName(); + private static final String TAG = "WallpaperService"; + + private Object mLock = new Object(); private static final File WALLPAPER_DIR = new File( "/data/data/com.android.settings/files"); private static final String WALLPAPER = "wallpaper"; private static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER); - private static final String PREFERENCES = "wallpaper-hints"; - - private static final String HINT_WIDTH = "hintWidth"; - private static final String HINT_HEIGHT = "hintHeight"; - /** * List of callbacks registered they should each be notified * when the wallpaper is changed. */ private final RemoteCallbackList<IWallpaperServiceCallback> mCallbacks = new RemoteCallbackList<IWallpaperServiceCallback>(); - + /** * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks * that the wallpaper has changed. The CREATE is triggered when there is no @@ -64,16 +73,24 @@ class WallpaperService extends IWallpaperService.Stub { * everytime the wallpaper is changed. */ private final FileObserver mWallpaperObserver = new FileObserver( - WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE) { + WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) { @Override public void onEvent(int event, String path) { - if (path == null) { - return; - } + synchronized (mLock) { + if (path == null) { + return; + } + + // changing the wallpaper means we'll need to back up the new one + long origId = Binder.clearCallingIdentity(); + BackupManager bm = new BackupManager(mContext); + bm.dataChanged(); + Binder.restoreCallingIdentity(origId); - File changedFile = new File(WALLPAPER_DIR, path); - if (WALLPAPER_FILE.equals(changedFile)) { - notifyCallbacks(); + File changedFile = new File(WALLPAPER_DIR, path); + if (WALLPAPER_FILE.equals(changedFile)) { + notifyCallbacksLocked(); + } } } }; @@ -82,17 +99,16 @@ class WallpaperService extends IWallpaperService.Stub { private int mWidth = -1; private int mHeight = -1; + private String mName = ""; public WallpaperService(Context context) { if (Config.LOGD) Log.d(TAG, "WallpaperService startup"); mContext = context; - createFilesDir(); + if (!WALLPAPER_DIR.exists()) { + WALLPAPER_DIR.mkdirs(); + } mWallpaperObserver.startWatching(); - - SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES, - Context.MODE_PRIVATE); - mWidth = preferences.getInt(HINT_WIDTH, -1); - mHeight = preferences.getInt(HINT_HEIGHT, -1); + loadSettingsLocked(); } @Override @@ -102,9 +118,11 @@ class WallpaperService extends IWallpaperService.Stub { } public void clearWallpaper() { - File f = WALLPAPER_FILE; - if (f.exists()) { - f.delete(); + synchronized (mLock) { + File f = WALLPAPER_FILE; + if (f.exists()) { + f.delete(); + } } } @@ -115,70 +133,62 @@ class WallpaperService extends IWallpaperService.Stub { throw new IllegalArgumentException("width and height must be > 0"); } - if (width != mWidth || height != mHeight) { - mWidth = width; - mHeight = height; - - SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES, - Context.MODE_PRIVATE); - - final SharedPreferences.Editor editor = preferences.edit(); - editor.putInt(HINT_WIDTH, width); - editor.putInt(HINT_HEIGHT, height); - editor.commit(); + synchronized (mLock) { + if (width != mWidth || height != mHeight) { + mWidth = width; + mHeight = height; + saveSettingsLocked(); + } } } public int getWidthHint() throws RemoteException { - return mWidth; + synchronized (mLock) { + return mWidth; + } } public int getHeightHint() throws RemoteException { - return mHeight; + synchronized (mLock) { + return mHeight; + } } public ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb) { - try { - mCallbacks.register(cb); - File f = WALLPAPER_FILE; - if (!f.exists()) { - return null; + synchronized (mLock) { + try { + mCallbacks.register(cb); + File f = WALLPAPER_FILE; + if (!f.exists()) { + return null; + } + return ParcelFileDescriptor.open(f, MODE_READ_ONLY); + } catch (FileNotFoundException e) { + /* Shouldn't happen as we check to see if the file exists */ + if (Config.LOGD) Log.d(TAG, "Error getting wallpaper", e); } - return ParcelFileDescriptor.open(f, MODE_READ_ONLY); - } catch (FileNotFoundException e) { - - /* Shouldn't happen as we check to see if the file exists */ - if (Config.LOGD) Log.d(TAG, "Error getting wallpaper", e); + return null; } - return null; } - public ParcelFileDescriptor setWallpaper() { + public ParcelFileDescriptor setWallpaper(String name) { checkPermission(android.Manifest.permission.SET_WALLPAPER); - try { - ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, - MODE_CREATE|MODE_READ_WRITE); - - // changing the wallpaper means we'll need to back up the new one - long origId = Binder.clearCallingIdentity(); - BackupManager bm = new BackupManager(mContext); - bm.dataChanged(); - Binder.restoreCallingIdentity(origId); - - return fd; - } catch (FileNotFoundException e) { - if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e); - } - return null; - } - - private void createFilesDir() { - if (!WALLPAPER_DIR.exists()) { - WALLPAPER_DIR.mkdirs(); + synchronized (mLock) { + if (name == null) name = ""; + mName = name; + saveSettingsLocked(); + try { + ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, + MODE_CREATE|MODE_READ_WRITE); + return fd; + } catch (FileNotFoundException e) { + if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e); + } + return null; } } - private void notifyCallbacks() { + private void notifyCallbacksLocked() { final int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; i++) { try { @@ -195,9 +205,185 @@ class WallpaperService extends IWallpaperService.Stub { } private void checkPermission(String permission) { - if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { + if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) { throw new SecurityException("Access denied to process: " + Binder.getCallingPid() + ", must have permission " + permission); } } + + private static JournaledFile makeJournaledFile() { + final String base = "/data/system/wallpaper_info.xml"; + return new JournaledFile(new File(base), new File(base + ".tmp")); + } + + private void saveSettingsLocked() { + JournaledFile journal = makeJournaledFile(); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(journal.chooseForWrite(), false); + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + + out.startTag(null, "wp"); + out.attribute(null, "width", Integer.toString(mWidth)); + out.attribute(null, "height", Integer.toString(mHeight)); + out.attribute(null, "name", mName); + out.endTag(null, "wp"); + + out.endDocument(); + stream.close(); + journal.commit(); + } catch (IOException e) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException ex) { + // Ignore + } + journal.rollback(); + } + } + + private void loadSettingsLocked() { + JournaledFile journal = makeJournaledFile(); + FileInputStream stream = null; + File file = journal.chooseForRead(); + boolean success = false; + try { + stream = new FileInputStream(file); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, null); + + int type; + int providerIndex = 0; + do { + type = parser.next(); + if (type == XmlPullParser.START_TAG) { + String tag = parser.getName(); + if ("wp".equals(tag)) { + mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); + mHeight = Integer.parseInt(parser.getAttributeValue(null, "height")); + mName = parser.getAttributeValue(null, "name"); + } + } + } while (type != XmlPullParser.END_DOCUMENT); + success = true; + } catch (NullPointerException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (NumberFormatException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (XmlPullParserException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (IOException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (IndexOutOfBoundsException e) { + Log.w(TAG, "failed parsing " + file, e); + } + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // Ignore + } + + if (!success) { + mWidth = -1; + mHeight = -1; + mName = ""; + } + } + + void settingsRestored() { + boolean success = false; + synchronized (mLock) { + loadSettingsLocked(); + // If there's a wallpaper name, we use that. If that can't be loaded, then we + // use the default. + if ("".equals(mName)) { + success = true; + } else { + success = restoreNamedResourceLocked(); + } + } + + if (!success) { + Log.e(TAG, "Failed to restore wallpaper: '" + mName + "'"); + mName = ""; + WALLPAPER_FILE.delete(); + } + saveSettingsLocked(); + } + + boolean restoreNamedResourceLocked() { + if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) { + String resName = mName.substring(4); + + String pkg = null; + int colon = resName.indexOf(':'); + if (colon > 0) { + pkg = resName.substring(0, colon); + } + + String ident = null; + int slash = resName.lastIndexOf('/'); + if (slash > 0) { + ident = resName.substring(slash+1); + } + + String type = null; + if (colon > 0 && slash > 0 && (slash-colon) > 1) { + type = resName.substring(colon+1, slash); + } + + if (pkg != null && ident != null && type != null) { + int resId = -1; + InputStream res = null; + FileOutputStream fos = null; + try { + Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); + Resources r = c.getResources(); + resId = r.getIdentifier(resName, null, null); + if (resId == 0) { + Log.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type + + " ident=" + ident); + return false; + } + + res = r.openRawResource(resId); + fos = new FileOutputStream(WALLPAPER_FILE); + + byte[] buffer = new byte[32768]; + int amt; + while ((amt=res.read(buffer)) > 0) { + fos.write(buffer, 0, amt); + } + // mWallpaperObserver will notice the close and send the change broadcast + + Log.d(TAG, "Restored wallpaper: " + resName); + return true; + } catch (NameNotFoundException e) { + Log.e(TAG, "Package name " + pkg + " not found"); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource not found: " + resId); + } catch (IOException e) { + Log.e(TAG, "IOException while restoring wallpaper ", e); + } finally { + if (res != null) { + try { + res.close(); + } catch (IOException ex) {} + } + if (fos != null) { + try { + fos.close(); + } catch (IOException ex) {} + } + } + } + } + return false; + } } |