Rename ApplicationContext to ContextImpl.

I've been wanting to do this for a long long time.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
new file mode 100644
index 0000000..5f89496
--- /dev/null
+++ b/core/java/android/app/ContextImpl.java
@@ -0,0 +1,2946 @@
+/*
+ * Copyright (C) 2006 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 android.app;
+
+import com.android.internal.policy.PolicyManager;
+import com.android.common.XmlUtils;
+import com.google.android.collect.Maps;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IIntentReceiver;
+import android.content.IntentSender;
+import android.content.ReceiverCallNotAllowedException;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.hardware.SensorManager;
+import android.location.ILocationManager;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.Uri;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+import android.os.Vibrator;
+import android.os.FileUtils.FileStatus;
+import android.storage.StorageManager;
+import android.telephony.TelephonyManager;
+import android.text.ClipboardManager;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.WindowManagerImpl;
+import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethodManager;
+import android.accounts.AccountManager;
+import android.accounts.IAccountManager;
+
+import com.android.internal.os.IDropBoxManagerService;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.Map.Entry;
+
+class ReceiverRestrictedContext extends ContextWrapper {
+    ReceiverRestrictedContext(Context base) {
+        super(base);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return registerReceiver(receiver, filter, null, null);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        throw new ReceiverCallNotAllowedException(
+                "IntentReceiver components are not allowed to register to receive intents");
+        //ex.fillInStackTrace();
+        //Log.e("IntentReceiver", ex.getMessage(), ex);
+        //return mContext.registerReceiver(receiver, filter, broadcastPermission,
+        //        scheduler);
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+        throw new ReceiverCallNotAllowedException(
+                "IntentReceiver components are not allowed to bind to services");
+        //ex.fillInStackTrace();
+        //Log.e("IntentReceiver", ex.getMessage(), ex);
+        //return mContext.bindService(service, interfaceName, conn, flags);
+    }
+}
+
+/**
+ * Common implementation of Context API, which provides the base
+ * context object for Activity and other application components.
+ */
+class ContextImpl extends Context {
+    private final static String TAG = "ApplicationContext";
+    private final static boolean DEBUG = false;
+    private final static boolean DEBUG_ICONS = false;
+
+    private static final Object sSync = new Object();
+    private static AlarmManager sAlarmManager;
+    private static PowerManager sPowerManager;
+    private static ConnectivityManager sConnectivityManager;
+    private static WifiManager sWifiManager;
+    private static LocationManager sLocationManager;
+    private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
+            new HashMap<File, SharedPreferencesImpl>();
+
+    private AudioManager mAudioManager;
+    /*package*/ ActivityThread.PackageInfo mPackageInfo;
+    private Resources mResources;
+    /*package*/ ActivityThread mMainThread;
+    private Context mOuterContext;
+    private IBinder mActivityToken = null;
+    private ApplicationContentResolver mContentResolver;
+    private int mThemeResource = 0;
+    private Resources.Theme mTheme = null;
+    private PackageManager mPackageManager;
+    private NotificationManager mNotificationManager = null;
+    private ActivityManager mActivityManager = null;
+    private WallpaperManager mWallpaperManager = null;
+    private Context mReceiverRestrictedContext = null;
+    private SearchManager mSearchManager = null;
+    private SensorManager mSensorManager = null;
+    private StorageManager mStorageManager = null;
+    private Vibrator mVibrator = null;
+    private LayoutInflater mLayoutInflater = null;
+    private StatusBarManager mStatusBarManager = null;
+    private TelephonyManager mTelephonyManager = null;
+    private ClipboardManager mClipboardManager = null;
+    private boolean mRestricted;
+    private AccountManager mAccountManager; // protected by mSync
+    private DropBoxManager mDropBoxManager = null;
+    private DevicePolicyManager mDevicePolicyManager = null;
+
+    private final Object mSync = new Object();
+
+    private File mDatabasesDir;
+    private File mPreferencesDir;
+    private File mFilesDir;
+    
+
+    private File mCacheDir;
+    
+    private static long sInstanceCount = 0;
+
+    private static final String[] EMPTY_FILE_LIST = {};
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        --sInstanceCount;
+    }
+
+    public static long getInstanceCount() {
+        return sInstanceCount;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        return mResources.getAssets();
+    }
+
+    @Override
+    public Resources getResources() {
+        return mResources;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        if (mPackageManager != null) {
+            return mPackageManager;
+        }
+
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm != null) {
+            // Doesn't matter if we make more than one instance.
+            return (mPackageManager = new ApplicationPackageManager(this, pm));
+        }
+
+        return null;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        return mMainThread.getLooper();
+    }
+    
+    @Override
+    public Context getApplicationContext() {
+        return mMainThread.getApplication();
+    }
+    
+    @Override
+    public void setTheme(int resid) {
+        mThemeResource = resid;
+    }
+    
+    @Override
+    public Resources.Theme getTheme() {
+        if (mTheme == null) {
+            if (mThemeResource == 0) {
+                mThemeResource = com.android.internal.R.style.Theme;
+            }
+            mTheme = mResources.newTheme();
+            mTheme.applyStyle(mThemeResource, true);
+        }
+        return mTheme;
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return mPackageInfo != null ?
+                mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
+    }
+
+    @Override
+    public String getPackageName() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getPackageName();
+        }
+        throw new RuntimeException("Not supported in system context");
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getApplicationInfo();
+        }
+        throw new RuntimeException("Not supported in system context");
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getResDir();
+        }
+        throw new RuntimeException("Not supported in system context");
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getAppDir();
+        }
+        throw new RuntimeException("Not supported in system context");
+    }
+    
+    private static File makeBackupFile(File prefsFile) {
+        return new File(prefsFile.getPath() + ".bak");
+    }
+
+    public File getSharedPrefsFile(String name) {
+        return makeFilename(getPreferencesDir(), name + ".xml");
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        SharedPreferencesImpl sp;
+        File f = getSharedPrefsFile(name);
+        synchronized (sSharedPrefs) {
+            sp = sSharedPrefs.get(f);
+            if (sp != null && !sp.hasFileChanged()) {
+                //Log.i(TAG, "Returning existing prefs " + name + ": " + sp);
+                return sp;
+            }
+        }
+        
+        FileInputStream str = null;
+        File backup = makeBackupFile(f);
+        if (backup.exists()) {
+            f.delete();
+            backup.renameTo(f);
+        }
+
+        // Debugging
+        if (f.exists() && !f.canRead()) {
+            Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
+        }
+        
+        Map map = null;
+        if (f.exists() && f.canRead()) {
+            try {
+                str = new FileInputStream(f);
+                map = XmlUtils.readMapXml(str);
+                str.close();
+            } catch (org.xmlpull.v1.XmlPullParserException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            } catch (IOException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            }
+        }
+
+        synchronized (sSharedPrefs) {
+            if (sp != null) {
+                //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);
+                sp.replace(map);
+            } else {
+                sp = sSharedPrefs.get(f);
+                if (sp == null) {
+                    sp = new SharedPreferencesImpl(f, mode, map);
+                    sSharedPrefs.put(f, sp);
+                }
+            }
+            return sp;
+        }
+    }
+
+    private File getPreferencesDir() {
+        synchronized (mSync) {
+            if (mPreferencesDir == null) {
+                mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
+            }
+            return mPreferencesDir;
+        }
+    }
+
+    @Override
+    public FileInputStream openFileInput(String name)
+        throws FileNotFoundException {
+        File f = makeFilename(getFilesDir(), name);
+        return new FileInputStream(f);
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String name, int mode)
+        throws FileNotFoundException {
+        final boolean append = (mode&MODE_APPEND) != 0;
+        File f = makeFilename(getFilesDir(), name);
+        try {
+            FileOutputStream fos = new FileOutputStream(f, append);
+            setFilePermissionsFromMode(f.getPath(), mode, 0);
+            return fos;
+        } catch (FileNotFoundException e) {
+        }
+
+        File parent = f.getParentFile();
+        parent.mkdir();
+        FileUtils.setPermissions(
+            parent.getPath(),
+            FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+            -1, -1);
+        FileOutputStream fos = new FileOutputStream(f, append);
+        setFilePermissionsFromMode(f.getPath(), mode, 0);
+        return fos;
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        File f = makeFilename(getFilesDir(), name);
+        return f.delete();
+    }
+
+    @Override
+    public File getFilesDir() {
+        synchronized (mSync) {
+            if (mFilesDir == null) {
+                mFilesDir = new File(getDataDirFile(), "files");
+            }
+            if (!mFilesDir.exists()) {
+                if(!mFilesDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create files directory");
+                    return null;
+                }
+                FileUtils.setPermissions(
+                        mFilesDir.getPath(),
+                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                        -1, -1);
+            }
+            return mFilesDir;
+        }
+    }
+    
+    @Override
+    public File getCacheDir() {
+        synchronized (mSync) {
+            if (mCacheDir == null) {
+                mCacheDir = new File(getDataDirFile(), "cache");
+            }
+            if (!mCacheDir.exists()) {
+                if(!mCacheDir.mkdirs()) {
+                    Log.w(TAG, "Unable to create cache directory");
+                    return null;
+                }
+                FileUtils.setPermissions(
+                        mCacheDir.getPath(),
+                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                        -1, -1);
+            }
+        }
+        return mCacheDir;
+    }
+    
+
+    @Override
+    public File getFileStreamPath(String name) {
+        return makeFilename(getFilesDir(), name);
+    }
+
+    @Override
+    public String[] fileList() {
+        final String[] list = getFilesDir().list();
+        return (list != null) ? list : EMPTY_FILE_LIST;
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+        File f = validateFilePath(name, true);
+        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
+        setFilePermissionsFromMode(f.getPath(), mode, 0);
+        return db;
+    }
+
+    @Override
+    public boolean deleteDatabase(String name) {
+        try {
+            File f = validateFilePath(name, false);
+            return f.delete();
+        } catch (Exception e) {
+        }
+        return false;
+    }
+
+    @Override
+    public File getDatabasePath(String name) {
+        return validateFilePath(name, false);
+    }
+
+    @Override
+    public String[] databaseList() {
+        final String[] list = getDatabasesDir().list();
+        return (list != null) ? list : EMPTY_FILE_LIST;
+    }
+
+    
+    private File getDatabasesDir() {
+        synchronized (mSync) {
+            if (mDatabasesDir == null) {
+                mDatabasesDir = new File(getDataDirFile(), "databases");
+            }
+            if (mDatabasesDir.getPath().equals("databases")) {
+                mDatabasesDir = new File("/data/system");
+            }
+            return mDatabasesDir;
+        }
+    }
+    
+    @Override
+    public Drawable getWallpaper() {
+        return getWallpaperManager().getDrawable();
+    }
+
+    @Override
+    public Drawable peekWallpaper() {
+        return getWallpaperManager().peekDrawable();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumWidth() {
+        return getWallpaperManager().getDesiredMinimumWidth();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumHeight() {
+        return getWallpaperManager().getDesiredMinimumHeight();
+    }
+
+    @Override
+    public void setWallpaper(Bitmap bitmap) throws IOException  {
+        getWallpaperManager().setBitmap(bitmap);
+    }
+
+    @Override
+    public void setWallpaper(InputStream data) throws IOException {
+        getWallpaperManager().setStream(data);
+    }
+
+    @Override
+    public void clearWallpaper() throws IOException {
+        getWallpaperManager().clear();
+    }
+
+    @Override
+    public void startActivity(Intent intent) {
+        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+            throw new AndroidRuntimeException(
+                    "Calling startActivity() from outside of an Activity "
+                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+                    + " Is this really what you want?");
+        }
+        mMainThread.getInstrumentation().execStartActivity(
+            getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent,
+            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+            throws IntentSender.SendIntentException {
+        try {
+            String resolvedType = null;
+            if (fillInIntent != null) {
+                resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
+            }
+            int result = ActivityManagerNative.getDefault()
+                .startActivityIntentSender(mMainThread.getApplicationThread(), intent,
+                        fillInIntent, resolvedType, null, null,
+                        0, flagsMask, flagsValues);
+            if (result == IActivityManager.START_CANCELED) {
+                throw new IntentSender.SendIntentException();
+            }
+            Instrumentation.checkStartActivityResult(result, null);
+        } catch (RemoteException e) {
+        }
+    }
+    
+    @Override
+    public void sendBroadcast(Intent intent) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, null, false, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, receiverPermission, false, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, receiverPermission, true, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        IIntentReceiver rd = null;
+        if (resultReceiver != null) {
+            if (mPackageInfo != null) {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = mPackageInfo.getReceiverDispatcher(
+                    resultReceiver, getOuterContext(), scheduler,
+                    mMainThread.getInstrumentation(), false);
+            } else {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+                        resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+            }
+        }
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+                initialCode, initialData, initialExtras, receiverPermission,
+                true, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent intent) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, null, false, true);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcast(Intent intent,
+            BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        IIntentReceiver rd = null;
+        if (resultReceiver != null) {
+            if (mPackageInfo != null) {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = mPackageInfo.getReceiverDispatcher(
+                    resultReceiver, getOuterContext(), scheduler,
+                    mMainThread.getInstrumentation(), false);
+            } else {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+                        resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+            }
+        }
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+                initialCode, initialData, initialExtras, null,
+                true, true);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent intent) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        if (resolvedType != null) {
+            intent = new Intent(intent);
+            intent.setDataAndType(intent.getData(), resolvedType);
+        }
+        try {
+            ActivityManagerNative.getDefault().unbroadcastIntent(
+                mMainThread.getApplicationThread(), intent);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return registerReceiver(receiver, filter, null, null);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        return registerReceiverInternal(receiver, filter, broadcastPermission,
+                scheduler, getOuterContext());
+    }
+
+    private Intent registerReceiverInternal(BroadcastReceiver receiver,
+            IntentFilter filter, String broadcastPermission,
+            Handler scheduler, Context context) {
+        IIntentReceiver rd = null;
+        if (receiver != null) {
+            if (mPackageInfo != null && context != null) {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = mPackageInfo.getReceiverDispatcher(
+                    receiver, context, scheduler,
+                    mMainThread.getInstrumentation(), true);
+            } else {
+                if (scheduler == null) {
+                    scheduler = mMainThread.getHandler();
+                }
+                rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+                        receiver, context, scheduler, null, false).getIIntentReceiver();
+            }
+        }
+        try {
+            return ActivityManagerNative.getDefault().registerReceiver(
+                    mMainThread.getApplicationThread(),
+                    rd, filter, broadcastPermission);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        if (mPackageInfo != null) {
+            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+                    getOuterContext(), receiver);
+            try {
+                ActivityManagerNative.getDefault().unregisterReceiver(rd);
+            } catch (RemoteException e) {
+            }
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+    }
+
+    @Override
+    public ComponentName startService(Intent service) {
+        try {
+            ComponentName cn = ActivityManagerNative.getDefault().startService(
+                mMainThread.getApplicationThread(), service,
+                service.resolveTypeIfNeeded(getContentResolver()));
+            if (cn != null && cn.getPackageName().equals("!")) {
+                throw new SecurityException(
+                        "Not allowed to start service " + service
+                        + " without permission " + cn.getClassName());
+            }
+            return cn;
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean stopService(Intent service) {
+        try {
+            int res = ActivityManagerNative.getDefault().stopService(
+                mMainThread.getApplicationThread(), service,
+                service.resolveTypeIfNeeded(getContentResolver()));
+            if (res < 0) {
+                throw new SecurityException(
+                        "Not allowed to stop service " + service);
+            }
+            return res != 0;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn,
+            int flags) {
+        IServiceConnection sd;
+        if (mPackageInfo != null) {
+            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
+                    mMainThread.getHandler(), flags);
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+        try {
+            int res = ActivityManagerNative.getDefault().bindService(
+                mMainThread.getApplicationThread(), getActivityToken(),
+                service, service.resolveTypeIfNeeded(getContentResolver()),
+                sd, flags);
+            if (res < 0) {
+                throw new SecurityException(
+                        "Not allowed to bind to service " + service);
+            }
+            return res != 0;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public void unbindService(ServiceConnection conn) {
+        if (mPackageInfo != null) {
+            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+                    getOuterContext(), conn);
+            try {
+                ActivityManagerNative.getDefault().unbindService(sd);
+            } catch (RemoteException e) {
+            }
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName className,
+            String profileFile, Bundle arguments) {
+        try {
+            return ActivityManagerNative.getDefault().startInstrumentation(
+                    className, profileFile, 0, arguments, null);
+        } catch (RemoteException e) {
+            // System has crashed, nothing we can do.
+        }
+        return false;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        if (WINDOW_SERVICE.equals(name)) {
+            return WindowManagerImpl.getDefault();
+        } else if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+            synchronized (mSync) {
+                LayoutInflater inflater = mLayoutInflater;
+                if (inflater != null) {
+                    return inflater;
+                }
+                mLayoutInflater = inflater =
+                    PolicyManager.makeNewLayoutInflater(getOuterContext());
+                return inflater;
+            }
+        } else if (ACTIVITY_SERVICE.equals(name)) {
+            return getActivityManager();
+        } else if (INPUT_METHOD_SERVICE.equals(name)) {
+            return InputMethodManager.getInstance(this);
+        } else if (ALARM_SERVICE.equals(name)) {
+            return getAlarmManager();
+        } else if (ACCOUNT_SERVICE.equals(name)) {
+            return getAccountManager();
+        } else if (POWER_SERVICE.equals(name)) {
+            return getPowerManager();
+        } else if (CONNECTIVITY_SERVICE.equals(name)) {
+            return getConnectivityManager();
+        } else if (WIFI_SERVICE.equals(name)) {
+            return getWifiManager();
+        } else if (NOTIFICATION_SERVICE.equals(name)) {
+            return getNotificationManager();
+        } else if (KEYGUARD_SERVICE.equals(name)) {
+            return new KeyguardManager();
+        } else if (ACCESSIBILITY_SERVICE.equals(name)) {
+            return AccessibilityManager.getInstance(this);
+        } else if (LOCATION_SERVICE.equals(name)) {
+            return getLocationManager();
+        } else if (SEARCH_SERVICE.equals(name)) {
+            return getSearchManager();
+        } else if (SENSOR_SERVICE.equals(name)) {
+            return getSensorManager();
+        } else if (STORAGE_SERVICE.equals(name)) {
+            return getStorageManager();
+        } else if (VIBRATOR_SERVICE.equals(name)) {
+            return getVibrator();
+        } else if (STATUS_BAR_SERVICE.equals(name)) {
+            synchronized (mSync) {
+                if (mStatusBarManager == null) {
+                    mStatusBarManager = new StatusBarManager(getOuterContext());
+                }
+                return mStatusBarManager;
+            }
+        } else if (AUDIO_SERVICE.equals(name)) {
+            return getAudioManager();
+        } else if (TELEPHONY_SERVICE.equals(name)) {
+            return getTelephonyManager();
+        } else if (CLIPBOARD_SERVICE.equals(name)) {
+            return getClipboardManager();
+        } else if (WALLPAPER_SERVICE.equals(name)) {
+            return getWallpaperManager();
+        } else if (DROPBOX_SERVICE.equals(name)) {
+            return getDropBoxManager();
+        } else if (DEVICE_POLICY_SERVICE.equals(name)) {
+            return getDevicePolicyManager();
+        }
+
+        return null;
+    }
+
+    private AccountManager getAccountManager() {
+        synchronized (mSync) {
+            if (mAccountManager == null) {
+                IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
+                IAccountManager service = IAccountManager.Stub.asInterface(b);
+                mAccountManager = new AccountManager(this, service);
+            }
+            return mAccountManager;
+        }
+    }
+
+    private ActivityManager getActivityManager() {
+        synchronized (mSync) {
+            if (mActivityManager == null) {
+                mActivityManager = new ActivityManager(getOuterContext(),
+                        mMainThread.getHandler());
+            }
+        }
+        return mActivityManager;
+    }
+
+    private AlarmManager getAlarmManager() {
+        synchronized (sSync) {
+            if (sAlarmManager == null) {
+                IBinder b = ServiceManager.getService(ALARM_SERVICE);
+                IAlarmManager service = IAlarmManager.Stub.asInterface(b);
+                sAlarmManager = new AlarmManager(service);
+            }
+        }
+        return sAlarmManager;
+    }
+
+    private PowerManager getPowerManager() {
+        synchronized (sSync) {
+            if (sPowerManager == null) {
+                IBinder b = ServiceManager.getService(POWER_SERVICE);
+                IPowerManager service = IPowerManager.Stub.asInterface(b);
+                sPowerManager = new PowerManager(service, mMainThread.getHandler());
+            }
+        }
+        return sPowerManager;
+    }
+
+    private ConnectivityManager getConnectivityManager()
+    {
+        synchronized (sSync) {
+            if (sConnectivityManager == null) {
+                IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                sConnectivityManager = new ConnectivityManager(service);
+            }
+        }
+        return sConnectivityManager;
+    }
+
+    private WifiManager getWifiManager()
+    {
+        synchronized (sSync) {
+            if (sWifiManager == null) {
+                IBinder b = ServiceManager.getService(WIFI_SERVICE);
+                IWifiManager service = IWifiManager.Stub.asInterface(b);
+                sWifiManager = new WifiManager(service, mMainThread.getHandler());
+            }
+        }
+        return sWifiManager;
+    }
+
+    private NotificationManager getNotificationManager() {
+        synchronized (mSync) {
+            if (mNotificationManager == null) {
+                mNotificationManager = new NotificationManager(
+                        new ContextThemeWrapper(getOuterContext(), com.android.internal.R.style.Theme_Dialog),
+                        mMainThread.getHandler());
+            }
+        }
+        return mNotificationManager;
+    }
+
+    private WallpaperManager getWallpaperManager() {
+        synchronized (mSync) {
+            if (mWallpaperManager == null) {
+                mWallpaperManager = new WallpaperManager(getOuterContext(),
+                        mMainThread.getHandler());
+            }
+        }
+        return mWallpaperManager;
+    }
+
+    private TelephonyManager getTelephonyManager() {
+        synchronized (mSync) {
+            if (mTelephonyManager == null) {
+                mTelephonyManager = new TelephonyManager(getOuterContext());
+            }
+        }
+        return mTelephonyManager;
+    }
+
+    private ClipboardManager getClipboardManager() {
+        synchronized (mSync) {
+            if (mClipboardManager == null) {
+                mClipboardManager = new ClipboardManager(getOuterContext(),
+                        mMainThread.getHandler());
+            }
+        }
+        return mClipboardManager;
+    }
+
+    private LocationManager getLocationManager() {
+        synchronized (sSync) {
+            if (sLocationManager == null) {
+                IBinder b = ServiceManager.getService(LOCATION_SERVICE);
+                ILocationManager service = ILocationManager.Stub.asInterface(b);
+                sLocationManager = new LocationManager(service);
+            }
+        }
+        return sLocationManager;
+    }
+
+    private SearchManager getSearchManager() {
+        synchronized (mSync) {
+            if (mSearchManager == null) {
+                mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler());
+            }
+        }
+        return mSearchManager;
+    }
+
+    private SensorManager getSensorManager() {
+        synchronized (mSync) {
+            if (mSensorManager == null) {
+                mSensorManager = new SensorManager(mMainThread.getHandler().getLooper());
+            }
+        }
+        return mSensorManager;
+    }
+
+    private StorageManager getStorageManager() {
+        synchronized (mSync) {
+            if (mStorageManager == null) {
+                try {
+                    mStorageManager = new StorageManager(mMainThread.getHandler().getLooper());
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Failed to create StorageManager", rex);
+                    mStorageManager = null;
+                }
+            }
+        }
+        return mStorageManager;
+    }
+
+    private Vibrator getVibrator() {
+        synchronized (mSync) {
+            if (mVibrator == null) {
+                mVibrator = new Vibrator();
+            }
+        }
+        return mVibrator;
+    }
+
+    private AudioManager getAudioManager()
+    {
+        if (mAudioManager == null) {
+            mAudioManager = new AudioManager(this);
+        }
+        return mAudioManager;
+    }
+
+    private DropBoxManager getDropBoxManager() {
+        synchronized (mSync) {
+            if (mDropBoxManager == null) {
+                IBinder b = ServiceManager.getService(DROPBOX_SERVICE);
+                IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
+                mDropBoxManager = new DropBoxManager(service);
+            }
+        }
+        return mDropBoxManager;
+    }
+
+    private DevicePolicyManager getDevicePolicyManager() {
+        synchronized (mSync) {
+            if (mDevicePolicyManager == null) {
+                mDevicePolicyManager = new DevicePolicyManager(this,
+                        mMainThread.getHandler());
+            }
+        }
+        return mDevicePolicyManager;
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        if (!Process.supportsProcesses()) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        try {
+            return ActivityManagerNative.getDefault().checkPermission(
+                    permission, pid, uid);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    @Override
+    public int checkCallingPermission(String permission) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        if (!Process.supportsProcesses()) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        int pid = Binder.getCallingPid();
+        if (pid != Process.myPid()) {
+            return checkPermission(permission, pid,
+                    Binder.getCallingUid());
+        }
+        return PackageManager.PERMISSION_DENIED;
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        return checkPermission(permission, Binder.getCallingPid(),
+                Binder.getCallingUid());
+    }
+
+    private void enforce(
+            String permission, int resultOfCheck,
+            boolean selfToo, int uid, String message) {
+        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    (message != null ? (message + ": ") : "") +
+                    (selfToo
+                     ? "Neither user " + uid + " nor current process has "
+                     : "User " + uid + " does not have ") +
+                    permission +
+                    ".");
+        }
+    }
+
+    public void enforcePermission(
+            String permission, int pid, int uid, String message) {
+        enforce(permission,
+                checkPermission(permission, pid, uid),
+                false,
+                uid,
+                message);
+    }
+
+    public void enforceCallingPermission(String permission, String message) {
+        enforce(permission,
+                checkCallingPermission(permission),
+                false,
+                Binder.getCallingUid(),
+                message);
+    }
+
+    public void enforceCallingOrSelfPermission(
+            String permission, String message) {
+        enforce(permission,
+                checkCallingOrSelfPermission(permission),
+                true,
+                Binder.getCallingUid(),
+                message);
+    }
+
+    @Override
+    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+         try {
+            ActivityManagerNative.getDefault().grantUriPermission(
+                    mMainThread.getApplicationThread(), toPackage, uri,
+                    modeFlags);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void revokeUriPermission(Uri uri, int modeFlags) {
+         try {
+            ActivityManagerNative.getDefault().revokeUriPermission(
+                    mMainThread.getApplicationThread(), uri,
+                    modeFlags);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        if (!Process.supportsProcesses()) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        try {
+            return ActivityManagerNative.getDefault().checkUriPermission(
+                    uri, pid, uid, modeFlags);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri uri, int modeFlags) {
+        if (!Process.supportsProcesses()) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        int pid = Binder.getCallingPid();
+        if (pid != Process.myPid()) {
+            return checkUriPermission(uri, pid,
+                    Binder.getCallingUid(), modeFlags);
+        }
+        return PackageManager.PERMISSION_DENIED;
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+        return checkUriPermission(uri, Binder.getCallingPid(),
+                Binder.getCallingUid(), modeFlags);
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, String readPermission,
+            String writePermission, int pid, int uid, int modeFlags) {
+        if (DEBUG) {
+            Log.i("foo", "checkUriPermission: uri=" + uri + "readPermission="
+                    + readPermission + " writePermission=" + writePermission
+                    + " pid=" + pid + " uid=" + uid + " mode" + modeFlags);
+        }
+        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+            if (readPermission == null
+                    || checkPermission(readPermission, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+        }
+        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+            if (writePermission == null
+                    || checkPermission(writePermission, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+        }
+        return uri != null ? checkUriPermission(uri, pid, uid, modeFlags)
+                : PackageManager.PERMISSION_DENIED;
+    }
+
+    private String uriModeFlagToString(int uriModeFlags) {
+        switch (uriModeFlags) {
+            case Intent.FLAG_GRANT_READ_URI_PERMISSION |
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION:
+                return "read and write";
+            case Intent.FLAG_GRANT_READ_URI_PERMISSION:
+                return "read";
+            case Intent.FLAG_GRANT_WRITE_URI_PERMISSION:
+                return "write";
+        }
+        throw new IllegalArgumentException(
+                "Unknown permission mode flags: " + uriModeFlags);
+    }
+
+    private void enforceForUri(
+            int modeFlags, int resultOfCheck, boolean selfToo,
+            int uid, Uri uri, String message) {
+        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    (message != null ? (message + ": ") : "") +
+                    (selfToo
+                     ? "Neither user " + uid + " nor current process has "
+                     : "User " + uid + " does not have ") +
+                    uriModeFlagToString(modeFlags) +
+                    " permission on " +
+                    uri +
+                    ".");
+        }
+    }
+
+    public void enforceUriPermission(
+            Uri uri, int pid, int uid, int modeFlags, String message) {
+        enforceForUri(
+                modeFlags, checkUriPermission(uri, pid, uid, modeFlags),
+                false, uid, uri, message);
+    }
+
+    public void enforceCallingUriPermission(
+            Uri uri, int modeFlags, String message) {
+        enforceForUri(
+                modeFlags, checkCallingUriPermission(uri, modeFlags),
+                false, Binder.getCallingUid(), uri, message);
+    }
+
+    public void enforceCallingOrSelfUriPermission(
+            Uri uri, int modeFlags, String message) {
+        enforceForUri(
+                modeFlags,
+                checkCallingOrSelfUriPermission(uri, modeFlags), true,
+                Binder.getCallingUid(), uri, message);
+    }
+
+    public void enforceUriPermission(
+            Uri uri, String readPermission, String writePermission,
+            int pid, int uid, int modeFlags, String message) {
+        enforceForUri(modeFlags,
+                      checkUriPermission(
+                              uri, readPermission, writePermission, pid, uid,
+                              modeFlags),
+                      false,
+                      uid,
+                      uri,
+                      message);
+    }
+
+    @Override
+    public Context createPackageContext(String packageName, int flags)
+        throws PackageManager.NameNotFoundException {
+        if (packageName.equals("system") || packageName.equals("android")) {
+            return new ContextImpl(mMainThread.getSystemContext());
+        }
+
+        ActivityThread.PackageInfo pi =
+            mMainThread.getPackageInfo(packageName, flags);
+        if (pi != null) {
+            ContextImpl c = new ContextImpl();
+            c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
+            c.init(pi, null, mMainThread, mResources);
+            if (c.mResources != null) {
+                return c;
+            }
+        }
+
+        // Should be a better exception.
+        throw new PackageManager.NameNotFoundException(
+            "Application package " + packageName + " not found");
+    }
+
+    @Override
+    public boolean isRestricted() {
+        return mRestricted;
+    }
+
+    private File getDataDirFile() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getDataDirFile();
+        }
+        throw new RuntimeException("Not supported in system context");
+    }
+
+    @Override
+    public File getDir(String name, int mode) {
+        name = "app_" + name;
+        File file = makeFilename(getDataDirFile(), name);
+        if (!file.exists()) {
+            file.mkdir();
+            setFilePermissionsFromMode(file.getPath(), mode,
+                    FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH);
+        }
+        return file;
+    }
+
+    static ContextImpl createSystemContext(ActivityThread mainThread) {
+        ContextImpl context = new ContextImpl();
+        context.init(Resources.getSystem(), mainThread);
+        return context;
+    }
+
+    ContextImpl() {
+        ++sInstanceCount;
+        mOuterContext = this;
+    }
+
+    /**
+     * Create a new ApplicationContext from an existing one.  The new one
+     * works and operates the same as the one it is copying.
+     *
+     * @param context Existing application context.
+     */
+    public ContextImpl(ContextImpl context) {
+        ++sInstanceCount;
+        mPackageInfo = context.mPackageInfo;
+        mResources = context.mResources;
+        mMainThread = context.mMainThread;
+        mContentResolver = context.mContentResolver;
+        mOuterContext = this;
+    }
+
+    final void init(ActivityThread.PackageInfo packageInfo,
+            IBinder activityToken, ActivityThread mainThread) {
+        init(packageInfo, activityToken, mainThread, null);
+    }
+
+    final void init(ActivityThread.PackageInfo packageInfo,
+                IBinder activityToken, ActivityThread mainThread,
+                Resources container) {
+        mPackageInfo = packageInfo;
+        mResources = mPackageInfo.getResources(mainThread);
+
+        if (container != null && container.getCompatibilityInfo().applicationScale !=
+            mResources.getCompatibilityInfo().applicationScale) {
+            if (DEBUG) {
+                Log.d(TAG, "loaded context has different scaling. Using container's" +
+                        " compatiblity info:" + container.getDisplayMetrics());
+            }
+            mResources = mainThread.getTopLevelResources(
+                    mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
+        }
+        mMainThread = mainThread;
+        mContentResolver = new ApplicationContentResolver(this, mainThread);
+
+        setActivityToken(activityToken);
+    }
+
+    final void init(Resources resources, ActivityThread mainThread) {
+        mPackageInfo = null;
+        mResources = resources;
+        mMainThread = mainThread;
+        mContentResolver = new ApplicationContentResolver(this, mainThread);
+    }
+
+    final void scheduleFinalCleanup(String who, String what) {
+        mMainThread.scheduleContextCleanup(this, who, what);
+    }
+
+    final void performFinalCleanup(String who, String what) {
+        //Log.i(TAG, "Cleanup up context: " + this);
+        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+    }
+
+    final Context getReceiverRestrictedContext() {
+        if (mReceiverRestrictedContext != null) {
+            return mReceiverRestrictedContext;
+        }
+        return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
+    }
+
+    final void setActivityToken(IBinder token) {
+        mActivityToken = token;
+    }
+    
+    final void setOuterContext(Context context) {
+        mOuterContext = context;
+    }
+    
+    final Context getOuterContext() {
+        return mOuterContext;
+    }
+    
+    final IBinder getActivityToken() {
+        return mActivityToken;
+    }
+
+    private static void setFilePermissionsFromMode(String name, int mode,
+            int extraPermissions) {
+        int perms = FileUtils.S_IRUSR|FileUtils.S_IWUSR
+            |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+            |extraPermissions;
+        if ((mode&MODE_WORLD_READABLE) != 0) {
+            perms |= FileUtils.S_IROTH;
+        }
+        if ((mode&MODE_WORLD_WRITEABLE) != 0) {
+            perms |= FileUtils.S_IWOTH;
+        }
+        if (DEBUG) {
+            Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode)
+                  + ", perms=0x" + Integer.toHexString(perms));
+        }
+        FileUtils.setPermissions(name, perms, -1, -1);
+    }
+
+    private File validateFilePath(String name, boolean createDirectory) {
+        File dir;
+        File f;
+
+        if (name.charAt(0) == File.separatorChar) {
+            String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar));
+            dir = new File(dirPath);
+            name = name.substring(name.lastIndexOf(File.separatorChar));
+            f = new File(dir, name);
+        } else {
+            dir = getDatabasesDir();
+            f = makeFilename(dir, name);
+        }
+
+        if (createDirectory && !dir.isDirectory() && dir.mkdir()) {
+            FileUtils.setPermissions(dir.getPath(),
+                FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                -1, -1);
+        }
+
+        return f;
+    }
+
+    private File makeFilename(File base, String name) {
+        if (name.indexOf(File.separatorChar) < 0) {
+            return new File(base, name);
+        }
+        throw new IllegalArgumentException(
+                "File " + name + " contains a path separator");
+    }
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+
+    private static final class ApplicationContentResolver extends ContentResolver {
+        public ApplicationContentResolver(Context context,
+                                          ActivityThread mainThread)
+        {
+            super(context);
+            mMainThread = mainThread;
+        }
+
+        @Override
+        protected IContentProvider acquireProvider(Context context, String name)
+        {
+            return mMainThread.acquireProvider(context, name);
+        }
+
+        @Override
+        public boolean releaseProvider(IContentProvider provider)
+        {
+            return mMainThread.releaseProvider(provider);
+        }
+        
+        private final ActivityThread mMainThread;
+    }
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+
+    /*package*/
+    static final class ApplicationPackageManager extends PackageManager {
+        @Override
+        public PackageInfo getPackageInfo(String packageName, int flags)
+                throws NameNotFoundException {
+            try {
+                PackageInfo pi = mPM.getPackageInfo(packageName, flags);
+                if (pi != null) {
+                    return pi;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(packageName);
+        }
+
+        @Override
+        public Intent getLaunchIntentForPackage(String packageName) {
+            // First see if the package has an INFO activity; the existence of
+            // such an activity is implied to be the desired front-door for the
+            // overall package (such as if it has multiple launcher entries).
+            Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+            intentToResolve.addCategory(Intent.CATEGORY_INFO);
+            intentToResolve.setPackage(packageName);
+            ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0);
+
+            // Otherwise, try to find a main launcher activity.
+            if (resolveInfo == null) {
+                // reuse the intent instance
+                intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+                intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+                intentToResolve.setPackage(packageName);
+                resolveInfo = resolveActivity(intentToResolve, 0);
+            }
+            if (resolveInfo == null) {
+                return null;
+            }
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClassName(packageName, resolveInfo.activityInfo.name);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return intent;
+        }
+
+        @Override
+        public int[] getPackageGids(String packageName)
+            throws NameNotFoundException {
+            try {
+                int[] gids = mPM.getPackageGids(packageName);
+                if (gids == null || gids.length > 0) {
+                    return gids;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(packageName);
+        }
+
+        @Override
+        public PermissionInfo getPermissionInfo(String name, int flags)
+            throws NameNotFoundException {
+            try {
+                PermissionInfo pi = mPM.getPermissionInfo(name, flags);
+                if (pi != null) {
+                    return pi;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(name);
+        }
+
+        @Override
+        public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
+                throws NameNotFoundException {
+            try {
+                List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags);
+                if (pi != null) {
+                    return pi;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(group);
+        }
+
+        @Override
+        public PermissionGroupInfo getPermissionGroupInfo(String name,
+                int flags) throws NameNotFoundException {
+            try {
+                PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
+                if (pgi != null) {
+                    return pgi;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(name);
+        }
+
+        @Override
+        public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+            try {
+                return mPM.getAllPermissionGroups(flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public ApplicationInfo getApplicationInfo(String packageName, int flags)
+            throws NameNotFoundException {
+            try {
+                ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags);
+                if (ai != null) {
+                    return ai;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(packageName);
+        }
+
+        @Override
+        public ActivityInfo getActivityInfo(ComponentName className, int flags)
+            throws NameNotFoundException {
+            try {
+                ActivityInfo ai = mPM.getActivityInfo(className, flags);
+                if (ai != null) {
+                    return ai;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(className.toString());
+        }
+
+        @Override
+        public ActivityInfo getReceiverInfo(ComponentName className, int flags)
+            throws NameNotFoundException {
+            try {
+                ActivityInfo ai = mPM.getReceiverInfo(className, flags);
+                if (ai != null) {
+                    return ai;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(className.toString());
+        }
+
+        @Override
+        public ServiceInfo getServiceInfo(ComponentName className, int flags)
+            throws NameNotFoundException {
+            try {
+                ServiceInfo si = mPM.getServiceInfo(className, flags);
+                if (si != null) {
+                    return si;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(className.toString());
+        }
+
+        @Override
+        public String[] getSystemSharedLibraryNames() {
+             try {
+                 return mPM.getSystemSharedLibraryNames();
+             } catch (RemoteException e) {
+                 throw new RuntimeException("Package manager has died", e);
+             }
+        }
+
+        @Override
+        public FeatureInfo[] getSystemAvailableFeatures() {
+            try {
+                return mPM.getSystemAvailableFeatures();
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+        
+        @Override
+        public boolean hasSystemFeature(String name) {
+            try {
+                return mPM.hasSystemFeature(name);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+        
+        @Override
+        public int checkPermission(String permName, String pkgName) {
+            try {
+                return mPM.checkPermission(permName, pkgName);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public boolean addPermission(PermissionInfo info) {
+            try {
+                return mPM.addPermission(info);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public void removePermission(String name) {
+            try {
+                mPM.removePermission(name);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public int checkSignatures(String pkg1, String pkg2) {
+            try {
+                return mPM.checkSignatures(pkg1, pkg2);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public int checkSignatures(int uid1, int uid2) {
+            try {
+                return mPM.checkUidSignatures(uid1, uid2);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public String[] getPackagesForUid(int uid) {
+            try {
+                return mPM.getPackagesForUid(uid);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public String getNameForUid(int uid) {
+            try {
+                return mPM.getNameForUid(uid);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+        
+        @Override
+        public int getUidForSharedUser(String sharedUserName) 
+                throws NameNotFoundException {
+            try {
+                int uid = mPM.getUidForSharedUser(sharedUserName);
+                if(uid != -1) {
+                    return uid;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+            throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
+        }
+
+        @Override
+        public List<PackageInfo> getInstalledPackages(int flags) {
+            try {
+                return mPM.getInstalledPackages(flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ApplicationInfo> getInstalledApplications(int flags) {
+            try {
+                return mPM.getInstalledApplications(flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public ResolveInfo resolveActivity(Intent intent, int flags) {
+            try {
+                return mPM.resolveIntent(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ResolveInfo> queryIntentActivities(Intent intent,
+                int flags) {
+            try {
+                return mPM.queryIntentActivities(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ResolveInfo> queryIntentActivityOptions(
+                ComponentName caller, Intent[] specifics, Intent intent,
+                int flags) {
+            final ContentResolver resolver = mContext.getContentResolver();
+
+            String[] specificTypes = null;
+            if (specifics != null) {
+                final int N = specifics.length;
+                for (int i=0; i<N; i++) {
+                    Intent sp = specifics[i];
+                    if (sp != null) {
+                        String t = sp.resolveTypeIfNeeded(resolver);
+                        if (t != null) {
+                            if (specificTypes == null) {
+                                specificTypes = new String[N];
+                            }
+                            specificTypes[i] = t;
+                        }
+                    }
+                }
+            }
+
+            try {
+                return mPM.queryIntentActivityOptions(caller, specifics,
+                    specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+            try {
+                return mPM.queryIntentReceivers(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public ResolveInfo resolveService(Intent intent, int flags) {
+            try {
+                return mPM.resolveService(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+            try {
+                return mPM.queryIntentServices(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public ProviderInfo resolveContentProvider(String name,
+                int flags) {
+            try {
+                return mPM.resolveContentProvider(name, flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public List<ProviderInfo> queryContentProviders(String processName,
+                int uid, int flags) {
+            try {
+                return mPM.queryContentProviders(processName, uid, flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override
+        public InstrumentationInfo getInstrumentationInfo(
+                ComponentName className, int flags)
+                throws NameNotFoundException {
+            try {
+                InstrumentationInfo ii = mPM.getInstrumentationInfo(
+                        className, flags);
+                if (ii != null) {
+                    return ii;
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+
+            throw new NameNotFoundException(className.toString());
+        }
+
+        @Override
+        public List<InstrumentationInfo> queryInstrumentation(
+                String targetPackage, int flags) {
+            try {
+                return mPM.queryInstrumentation(targetPackage, flags);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        @Override public Drawable getDrawable(String packageName, int resid,
+                ApplicationInfo appInfo) {
+            ResourceName name = new ResourceName(packageName, resid);
+            Drawable dr = getCachedIcon(name);
+            if (dr != null) {
+                return dr;
+            }
+            if (appInfo == null) {
+                try {
+                    appInfo = getApplicationInfo(packageName, 0);
+                } catch (NameNotFoundException e) {
+                    return null;
+                }
+            }
+            try {
+                Resources r = getResourcesForApplication(appInfo);
+                dr = r.getDrawable(resid);
+                if (false) {
+                    RuntimeException e = new RuntimeException("here");
+                    e.fillInStackTrace();
+                    Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resid)
+                            + " from package " + packageName
+                            + ": app scale=" + r.getCompatibilityInfo().applicationScale
+                            + ", caller scale=" + mContext.getResources().getCompatibilityInfo().applicationScale,
+                            e);
+                }
+                if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
+                        + Integer.toHexString(resid) + " from " + r
+                        + ": " + dr);
+                putCachedIcon(name, dr);
+                return dr;
+            } catch (NameNotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for"
+                        + appInfo.packageName);
+            } catch (RuntimeException e) {
+                // If an exception was thrown, fall through to return
+                // default icon.
+                Log.w("PackageManager", "Failure retrieving icon 0x"
+                        + Integer.toHexString(resid) + " in package "
+                        + packageName, e);
+            }
+            return null;
+        }
+
+        @Override public Drawable getActivityIcon(ComponentName activityName)
+                throws NameNotFoundException {
+            return getActivityInfo(activityName, 0).loadIcon(this);
+        }
+
+        @Override public Drawable getActivityIcon(Intent intent)
+                throws NameNotFoundException {
+            if (intent.getComponent() != null) {
+                return getActivityIcon(intent.getComponent());
+            }
+
+            ResolveInfo info = resolveActivity(
+                intent, PackageManager.MATCH_DEFAULT_ONLY);
+            if (info != null) {
+                return info.activityInfo.loadIcon(this);
+            }
+
+            throw new NameNotFoundException(intent.toURI());
+        }
+
+        @Override public Drawable getDefaultActivityIcon() {
+            return Resources.getSystem().getDrawable(
+                com.android.internal.R.drawable.sym_def_app_icon);
+        }
+
+        @Override public Drawable getApplicationIcon(ApplicationInfo info) {
+            final int icon = info.icon;
+            if (icon != 0) {
+                ResourceName name = new ResourceName(info, icon);
+                Drawable dr = getCachedIcon(name);
+                if (dr != null) {
+                    return dr;
+                }
+                try {
+                    Resources r = getResourcesForApplication(info);
+                    dr = r.getDrawable(icon);
+                    if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
+                            + Integer.toHexString(icon) + " from " + r
+                            + ": " + dr);
+                    putCachedIcon(name, dr);
+                    return dr;
+                } catch (NameNotFoundException e) {
+                    Log.w("PackageManager", "Failure retrieving resources for"
+                            + info.packageName);
+                } catch (RuntimeException e) {
+                    // If an exception was thrown, fall through to return
+                    // default icon.
+                    Log.w("PackageManager", "Failure retrieving app icon", e);
+                }
+            }
+            return getDefaultActivityIcon();
+        }
+
+        @Override public Drawable getApplicationIcon(String packageName)
+                throws NameNotFoundException {
+            return getApplicationIcon(getApplicationInfo(packageName, 0));
+        }
+
+        @Override public Resources getResourcesForActivity(
+                ComponentName activityName) throws NameNotFoundException {
+            return getResourcesForApplication(
+                getActivityInfo(activityName, 0).applicationInfo);
+        }
+
+        @Override public Resources getResourcesForApplication(
+                ApplicationInfo app) throws NameNotFoundException {
+            if (app.packageName.equals("system")) {
+                return mContext.mMainThread.getSystemContext().getResources();
+            }
+            Resources r = mContext.mMainThread.getTopLevelResources(
+                    app.uid == Process.myUid() ? app.sourceDir
+                    : app.publicSourceDir, mContext.mPackageInfo);
+            if (r != null) {
+                return r;
+            }
+            throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
+        }
+
+        @Override public Resources getResourcesForApplication(
+                String appPackageName) throws NameNotFoundException {
+            return getResourcesForApplication(
+                getApplicationInfo(appPackageName, 0));
+        }
+
+        int mCachedSafeMode = -1;
+        @Override public boolean isSafeMode() {
+            try {
+                if (mCachedSafeMode < 0) {
+                    mCachedSafeMode = mPM.isSafeMode() ? 1 : 0;
+                }
+                return mCachedSafeMode != 0;
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+
+        static void configurationChanged() {
+            synchronized (sSync) {
+                sIconCache.clear();
+                sStringCache.clear();
+            }
+        }
+
+        ApplicationPackageManager(ContextImpl context,
+                IPackageManager pm) {
+            mContext = context;
+            mPM = pm;
+        }
+
+        private Drawable getCachedIcon(ResourceName name) {
+            synchronized (sSync) {
+                WeakReference<Drawable> wr = sIconCache.get(name);
+                if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for "
+                        + name + ": " + wr);
+                if (wr != null) {   // we have the activity
+                    Drawable dr = wr.get();
+                    if (dr != null) {
+                        if (DEBUG_ICONS) Log.v(TAG, "Get cached drawable for "
+                                + name + ": " + dr);
+                        return dr;
+                    }
+                    // our entry has been purged
+                    sIconCache.remove(name);
+                }
+            }
+            return null;
+        }
+
+        private void establishPackageRemovedReceiver() {
+            // mContext.registerReceiverInternal() winds up acquiring the
+            // main ActivityManagerService.this lock.  If we hold our usual
+            // sSync global lock at the same time, we impose a required ordering
+            // on those two locks, which is not good for deadlock prevention.
+            // Use a dedicated lock around initialization of
+            // sPackageRemovedReceiver to avoid this.
+            synchronized (sPackageRemovedSync) {
+                if (sPackageRemovedReceiver == null) {
+                    sPackageRemovedReceiver = new PackageRemovedReceiver();
+                    IntentFilter filter = new IntentFilter(
+                            Intent.ACTION_PACKAGE_REMOVED);
+                    filter.addDataScheme("package");
+                    mContext.registerReceiverInternal(sPackageRemovedReceiver,
+                            filter, null, null, null);
+                    // Register for events related to sdcard installation.
+                    IntentFilter sdFilter = new IntentFilter();
+                    sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+                    mContext.registerReceiverInternal(sPackageRemovedReceiver,
+                            sdFilter, null, null, null);
+                }
+            }
+        }
+        
+        private void putCachedIcon(ResourceName name, Drawable dr) {
+            establishPackageRemovedReceiver();
+
+            synchronized (sSync) {
+                sIconCache.put(name, new WeakReference<Drawable>(dr));
+                if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable for "
+                        + name + ": " + dr);
+            }
+        }
+
+        private static final class PackageRemovedReceiver extends BroadcastReceiver {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String pkgList[] = null;
+                String action = intent.getAction();
+                boolean immediateGc = false;
+                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    immediateGc = true;
+                } else {
+                    Uri data = intent.getData();
+                    if (data != null) {
+                        String ssp = data.getSchemeSpecificPart();
+                        if (ssp != null) {
+                            pkgList = new String[] { ssp };
+                        }
+                    }
+                }
+                if (pkgList != null && (pkgList.length > 0)) {
+                    boolean needCleanup = false;
+                    boolean hasPkgInfo = false;
+                    for (String ssp : pkgList) {
+                        synchronized (sSync) {
+                            Iterator<ResourceName> it = sIconCache.keySet().iterator();
+                            while (it.hasNext()) {
+                                ResourceName nm = it.next();
+                                if (nm.packageName.equals(ssp)) {
+                                    //Log.i(TAG, "Removing cached drawable for " + nm);
+                                    it.remove();
+                                    needCleanup = true;
+                                }
+                            }
+                            it = sStringCache.keySet().iterator();
+                            while (it.hasNext()) {
+                                ResourceName nm = it.next();
+                                if (nm.packageName.equals(ssp)) {
+                                    //Log.i(TAG, "Removing cached string for " + nm);
+                                    it.remove();
+                                    needCleanup = true;
+                                }
+                            }
+                        }
+                        if (!hasPkgInfo) {
+                            hasPkgInfo = ActivityThread.currentActivityThread().hasPackageInfo(ssp);
+                        }
+                    }
+                    if (needCleanup || hasPkgInfo) {
+                        if (immediateGc) {
+                            // Schedule an immediate gc.
+                            Runtime.getRuntime().gc();
+                        } else {
+                            ActivityThread.currentActivityThread().scheduleGcIdler();
+                        }
+                    }
+                }
+            }
+        }
+
+        private static final class ResourceName {
+            final String packageName;
+            final int iconId;
+
+            ResourceName(String _packageName, int _iconId) {
+                packageName = _packageName;
+                iconId = _iconId;
+            }
+
+            ResourceName(ApplicationInfo aInfo, int _iconId) {
+                this(aInfo.packageName, _iconId);
+            }
+
+            ResourceName(ComponentInfo cInfo, int _iconId) {
+                this(cInfo.applicationInfo.packageName, _iconId);
+            }
+
+            ResourceName(ResolveInfo rInfo, int _iconId) {
+                this(rInfo.activityInfo.applicationInfo.packageName, _iconId);
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+
+                ResourceName that = (ResourceName) o;
+
+                if (iconId != that.iconId) return false;
+                return !(packageName != null ?
+                        !packageName.equals(that.packageName) : that.packageName != null);
+
+            }
+
+            @Override
+            public int hashCode() {
+                int result;
+                result = packageName.hashCode();
+                result = 31 * result + iconId;
+                return result;
+            }
+
+            @Override
+            public String toString() {
+                return "{ResourceName " + packageName + " / " + iconId + "}";
+            }
+        }
+
+        private CharSequence getCachedString(ResourceName name) {
+            synchronized (sSync) {
+                WeakReference<CharSequence> wr = sStringCache.get(name);
+                if (wr != null) {   // we have the activity
+                    CharSequence cs = wr.get();
+                    if (cs != null) {
+                        return cs;
+                    }
+                    // our entry has been purged
+                    sStringCache.remove(name);
+                }
+            }
+            return null;
+        }
+
+        private void putCachedString(ResourceName name, CharSequence cs) {
+            establishPackageRemovedReceiver();
+
+            synchronized (sSync) {
+                sStringCache.put(name, new WeakReference<CharSequence>(cs));
+            }
+        }
+
+        private CharSequence getLabel(ResourceName name, ApplicationInfo app, int id) {
+            CharSequence cs = getCachedString(name);
+            if (cs != null) {
+                return cs;
+            }
+            try {
+                Resources r = getResourcesForApplication(app);
+                cs = r.getText(id);
+                putCachedString(name, cs);
+            } catch (NameNotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for"
+                        + app.packageName);
+            } catch (RuntimeException e) {
+                // If an exception was thrown, fall through to return null
+                Log.w("ApplicationInfo", "Failure retrieving activity name", e);
+            }
+            return cs;
+        }
+
+        @Override
+        public CharSequence getText(String packageName, int resid,
+                ApplicationInfo appInfo) {
+            ResourceName name = new ResourceName(packageName, resid);
+            CharSequence text = getCachedString(name);
+            if (text != null) {
+                return text;
+            }
+            if (appInfo == null) {
+                try {
+                    appInfo = getApplicationInfo(packageName, 0);
+                } catch (NameNotFoundException e) {
+                    return null;
+                }
+            }
+            try {
+                Resources r = getResourcesForApplication(appInfo);
+                text = r.getText(resid);
+                putCachedString(name, text);
+                return text;
+            } catch (NameNotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for"
+                        + appInfo.packageName);
+            } catch (RuntimeException e) {
+                // If an exception was thrown, fall through to return
+                // default icon.
+                Log.w("PackageManager", "Failure retrieving text 0x"
+                        + Integer.toHexString(resid) + " in package "
+                        + packageName, e);
+            }
+            return null;
+        }
+
+        @Override
+        public XmlResourceParser getXml(String packageName, int resid,
+                ApplicationInfo appInfo) {
+            if (appInfo == null) {
+                try {
+                    appInfo = getApplicationInfo(packageName, 0);
+                } catch (NameNotFoundException e) {
+                    return null;
+                }
+            }
+            try {
+                Resources r = getResourcesForApplication(appInfo);
+                return r.getXml(resid);
+            } catch (RuntimeException e) {
+                // If an exception was thrown, fall through to return
+                // default icon.
+                Log.w("PackageManager", "Failure retrieving xml 0x"
+                        + Integer.toHexString(resid) + " in package "
+                        + packageName, e);
+            } catch (NameNotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for"
+                        + appInfo.packageName);
+            }
+            return null;
+        }
+
+        @Override
+        public CharSequence getApplicationLabel(ApplicationInfo info) {
+            if (info.nonLocalizedLabel != null) {
+                return info.nonLocalizedLabel;
+            }
+            final int id = info.labelRes;
+            if (id != 0) {
+                CharSequence cs = getLabel(new ResourceName(info, id), info, id);
+                if (cs != null) {
+                    return cs;
+                }
+            }
+            return info.packageName;
+        }
+
+        @Override
+        public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
+                String installerPackageName) {
+            try {
+                mPM.installPackage(packageURI, observer, flags, installerPackageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public String getInstallerPackageName(String packageName) {
+            try {
+                return mPM.getInstallerPackageName(packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+            return null;
+        }
+
+        @Override
+        public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
+            try {
+                mPM.deletePackage(packageName, observer, flags);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        @Override
+        public void clearApplicationUserData(String packageName, 
+                IPackageDataObserver observer) {
+            try {
+                mPM.clearApplicationUserData(packageName, observer);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        @Override
+        public void deleteApplicationCacheFiles(String packageName, 
+                IPackageDataObserver observer) {
+            try {
+                mPM.deleteApplicationCacheFiles(packageName, observer);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        @Override
+        public void freeStorageAndNotify(long idealStorageSize, IPackageDataObserver observer) {
+            try {
+                mPM.freeStorageAndNotify(idealStorageSize, observer);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public void freeStorage(long freeStorageSize, IntentSender pi) {
+            try {
+                mPM.freeStorage(freeStorageSize, pi);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        
+        @Override
+        public void getPackageSizeInfo(String packageName, 
+                IPackageStatsObserver observer) {
+            try {
+                mPM.getPackageSizeInfo(packageName, observer);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        @Override
+        public void addPackageToPreferred(String packageName) {
+            try {
+                mPM.addPackageToPreferred(packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public void removePackageFromPreferred(String packageName) {
+            try {
+                mPM.removePackageFromPreferred(packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public List<PackageInfo> getPreferredPackages(int flags) {
+            try {
+                return mPM.getPreferredPackages(flags);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+            return new ArrayList<PackageInfo>();
+        }
+
+        @Override
+        public void addPreferredActivity(IntentFilter filter,
+                int match, ComponentName[] set, ComponentName activity) {
+            try {
+                mPM.addPreferredActivity(filter, match, set, activity);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        
+        @Override
+        public void replacePreferredActivity(IntentFilter filter,
+                int match, ComponentName[] set, ComponentName activity) {
+            try {
+                mPM.replacePreferredActivity(filter, match, set, activity);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public void clearPackagePreferredActivities(String packageName) {
+            try {
+                mPM.clearPackagePreferredActivities(packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        
+        @Override
+        public int getPreferredActivities(List<IntentFilter> outFilters,
+                List<ComponentName> outActivities, String packageName) {
+            try {
+                return mPM.getPreferredActivities(outFilters, outActivities, packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+            return 0;
+        }
+        
+        @Override
+        public void setComponentEnabledSetting(ComponentName componentName,
+                int newState, int flags) {
+            try {
+                mPM.setComponentEnabledSetting(componentName, newState, flags);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
+        public int getComponentEnabledSetting(ComponentName componentName) {
+            try {
+                return mPM.getComponentEnabledSetting(componentName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+            return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        }
+
+        @Override
+        public void setApplicationEnabledSetting(String packageName,
+                int newState, int flags) {
+            try {
+                mPM.setApplicationEnabledSetting(packageName, newState, flags);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+        
+        @Override
+        public int getApplicationEnabledSetting(String packageName) {
+            try {
+                return mPM.getApplicationEnabledSetting(packageName);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+            return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        }
+
+        // Constants related to app heuristics
+        // No-installation limit for internal flash: 10% or less space available
+        private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
+
+        // SD-to-internal app size threshold: currently set to 1 MB
+        private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
+
+        @Override
+        public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+            // Initial implementation:
+            // Package size = code size + cache size + data size
+            // If code size > 1 MB, install on SD card.
+            // Else install on internal NAND flash, unless space on NAND is less than 10%
+
+            if ((packageURI == null) || (appInfo == null)) {
+                return INSTALL_PARSE_FAILED_NOT_APK;
+            }
+
+            StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath());
+            StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+
+            long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() *
+                    (long)internalFlashStats.getBlockSize();
+            long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() *
+                    (long)internalFlashStats.getBlockSize();
+            long availSDSize = (long)sdcardStats.getAvailableBlocks() *
+                    (long)sdcardStats.getBlockSize();
+
+            double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
+
+            final String archiveFilePath = packageURI.getPath();
+            File apkFile = new File(archiveFilePath);
+            long pkgLen = apkFile.length();
+
+            // Consider application flags preferences as well...
+            boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
+
+            // These are not very precise measures, but I guess it is hard to estimate sizes
+            // before installing the package.
+            // As a shortcut, I am assuming that the package fits on NAND flash if the available
+            // space is three times that of the APK size. For SD, we only worry about the APK size.
+            // Since packages are downloaded into SD, this might not even be necessary.
+            boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
+            boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
+
+            // Does not fit, recommend no installation.
+            if (!fitsOnSD && !fitsOnInternalFlash) {
+                return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
+
+            if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
+                // recommend internal NAND likely
+                if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
+                    // Low space on NAND (<10%) - install on SD
+                    return INSTALL_ON_SDCARD;
+                }
+                return INSTALL_ON_INTERNAL_FLASH;
+            } else {
+                if (fitsOnSD) {
+                    // Recommend SD card
+                    return INSTALL_ON_SDCARD;
+                } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
+                        !(installOnlyOnSD)) {
+                    return INSTALL_ON_INTERNAL_FLASH;
+                } else {
+                    return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                }
+            }
+        }
+
+        private final ContextImpl mContext;
+        private final IPackageManager mPM;
+
+        private static final Object sSync = new Object();
+        private static final Object sPackageRemovedSync = new Object();
+        private static BroadcastReceiver sPackageRemovedReceiver;
+        private static HashMap<ResourceName, WeakReference<Drawable> > sIconCache
+                = new HashMap<ResourceName, WeakReference<Drawable> >();
+        private static HashMap<ResourceName, WeakReference<CharSequence> > sStringCache
+                = new HashMap<ResourceName, WeakReference<CharSequence> >();
+    }
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+
+    private static final class SharedPreferencesImpl implements SharedPreferences {
+
+        private final File mFile;
+        private final File mBackupFile;
+        private final int mMode;
+        private Map mMap;
+        private final FileStatus mFileStatus = new FileStatus();
+        private long mTimestamp;
+
+        private static final Object mContent = new Object();
+        private WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
+
+        SharedPreferencesImpl(
+            File file, int mode, Map initialContents) {
+            mFile = file;
+            mBackupFile = makeBackupFile(file);
+            mMode = mode;
+            mMap = initialContents != null ? initialContents : new HashMap();
+            if (FileUtils.getFileStatus(file.getPath(), mFileStatus)) {
+                mTimestamp = mFileStatus.mtime;
+            }
+            mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
+        }
+
+        public boolean hasFileChanged() {
+            synchronized (this) {
+                if (!FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
+                    return true;
+                }
+                return mTimestamp != mFileStatus.mtime;
+            }
+        }
+        
+        public void replace(Map newContents) {
+            if (newContents != null) {
+                synchronized (this) {
+                    mMap = newContents;
+                }
+            }
+        }
+        
+        public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+            synchronized(this) {
+                mListeners.put(listener, mContent);
+            }
+        }
+
+        public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+            synchronized(this) {
+                mListeners.remove(listener);
+            }
+        }
+
+        public Map<String, ?> getAll() {
+            synchronized(this) {
+                //noinspection unchecked
+                return new HashMap(mMap);
+            }
+        }
+
+        public String getString(String key, String defValue) {
+            synchronized (this) {
+                String v = (String)mMap.get(key);
+                return v != null ? v : defValue;
+            }
+        }
+
+        public int getInt(String key, int defValue) {
+            synchronized (this) {
+                Integer v = (Integer)mMap.get(key);
+                return v != null ? v : defValue;
+            }
+        }
+        public long getLong(String key, long defValue) {
+            synchronized (this) {
+                Long v = (Long) mMap.get(key);
+                return v != null ? v : defValue;
+            }
+        }
+        public float getFloat(String key, float defValue) {
+            synchronized (this) {
+                Float v = (Float)mMap.get(key);
+                return v != null ? v : defValue;
+            }
+        }
+        public boolean getBoolean(String key, boolean defValue) {
+            synchronized (this) {
+                Boolean v = (Boolean)mMap.get(key);
+                return v != null ? v : defValue;
+            }
+        }
+
+        public boolean contains(String key) {
+            synchronized (this) {
+                return mMap.containsKey(key);
+            }
+        }
+
+        public final class EditorImpl implements Editor {
+            private final Map<String, Object> mModified = Maps.newHashMap();
+            private boolean mClear = false;
+
+            public Editor putString(String key, String value) {
+                synchronized (this) {
+                    mModified.put(key, value);
+                    return this;
+                }
+            }
+            public Editor putInt(String key, int value) {
+                synchronized (this) {
+                    mModified.put(key, value);
+                    return this;
+                }
+            }
+            public Editor putLong(String key, long value) {
+                synchronized (this) {
+                    mModified.put(key, value);
+                    return this;
+                }
+            }
+            public Editor putFloat(String key, float value) {
+                synchronized (this) {
+                    mModified.put(key, value);
+                    return this;
+                }
+            }
+            public Editor putBoolean(String key, boolean value) {
+                synchronized (this) {
+                    mModified.put(key, value);
+                    return this;
+                }
+            }
+
+            public Editor remove(String key) {
+                synchronized (this) {
+                    mModified.put(key, this);
+                    return this;
+                }
+            }
+
+            public Editor clear() {
+                synchronized (this) {
+                    mClear = true;
+                    return this;
+                }
+            }
+
+            public boolean commit() {
+                boolean returnValue;
+
+                boolean hasListeners;
+                List<String> keysModified = null;
+                Set<OnSharedPreferenceChangeListener> listeners = null;
+
+                synchronized (SharedPreferencesImpl.this) {
+                    hasListeners = mListeners.size() > 0;
+                    if (hasListeners) {
+                        keysModified = new ArrayList<String>();
+                        listeners =
+                                new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
+                    }
+
+                    synchronized (this) {
+                        if (mClear) {
+                            mMap.clear();
+                            mClear = false;
+                        }
+
+                        for (Entry<String, Object> e : mModified.entrySet()) {
+                            String k = e.getKey();
+                            Object v = e.getValue();
+                            if (v == this) {
+                                mMap.remove(k);
+                            } else {
+                                mMap.put(k, v);
+                            }
+
+                            if (hasListeners) {
+                                keysModified.add(k);
+                            }
+                        }
+
+                        mModified.clear();
+                    }
+
+                    returnValue = writeFileLocked();
+                }
+
+                if (hasListeners) {
+                    for (int i = keysModified.size() - 1; i >= 0; i--) {
+                        final String key = keysModified.get(i);
+                        for (OnSharedPreferenceChangeListener listener : listeners) {
+                            if (listener != null) {
+                                listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
+                            }
+                        }
+                    }
+                }
+
+                return returnValue;
+            }
+        }
+
+        public Editor edit() {
+            return new EditorImpl();
+        }
+        
+        private FileOutputStream createFileOutputStream(File file) {
+            FileOutputStream str = null;
+            try {
+                str = new FileOutputStream(file);
+            } catch (FileNotFoundException e) {
+                File parent = file.getParentFile();
+                if (!parent.mkdir()) {
+                    Log.e(TAG, "Couldn't create directory for SharedPreferences file " + file);
+                    return null;
+                }
+                FileUtils.setPermissions(
+                    parent.getPath(),
+                    FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                    -1, -1);
+                try {
+                    str = new FileOutputStream(file);
+                } catch (FileNotFoundException e2) {
+                    Log.e(TAG, "Couldn't create SharedPreferences file " + file, e2);
+                }
+            }
+            return str;
+        }
+
+        private boolean writeFileLocked() {
+            // Rename the current file so it may be used as a backup during the next read
+            if (mFile.exists()) {
+                if (!mFile.renameTo(mBackupFile)) {
+                    Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile);
+                    return false;
+                }
+            }
+            
+            // Attempt to write the file, delete the backup and return true as atomically as
+            // possible.  If any exception occurs, delete the new file; next time we will restore
+            // from the backup.
+            try {
+                FileOutputStream str = createFileOutputStream(mFile);
+                if (str == null) {
+                    return false;
+                }
+                XmlUtils.writeMapXml(mMap, str);
+                str.close();
+                setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
+                if (FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
+                    mTimestamp = mFileStatus.mtime;
+                }
+                
+                // Writing was successful, delete the backup file if there is one.
+                mBackupFile.delete();
+                return true;
+            } catch (XmlPullParserException e) {
+                Log.w(TAG, "writeFileLocked: Got exception:", e);
+            } catch (IOException e) {
+                Log.w(TAG, "writeFileLocked: Got exception:", e);
+            }
+            // Clean up an unsuccessfully written file
+            if (mFile.exists()) {
+                if (!mFile.delete()) {
+                    Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
+                }
+            }
+            return false;
+        }
+    }
+}