Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 537186e9 authored by Clark Scheff's avatar Clark Scheff
Browse files

Themes: Only process themes that are applied during boot

Currently, the PackageManagerService will process all themes and
ensure their resources are compiled and available to the system.
This is highly inefficient and can bog the system down when booting.

This patch will only process the themes that are currently applied
durring boot and lets the ThemeService take care of queuing up the
others for processing.   This reduces boot time considerably,
especially when the user has many themes installed.

Change-Id: I52168a1751ad3bfc458048dd1558ad71cc16317a
parent f5fe065d
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1416,6 +1416,20 @@ final class ApplicationPackageManager extends PackageManager {
        }
    }

    /**
     * @hide
     */
    @Override
    public int processThemeResources(String themePkgName) {
        try {
            return mPM.processThemeResources(themePkgName);
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to process theme resources for " + themePkgName, e);
        }

        return 0;
    }

    @Override
    public void setComponentProtectedSetting(ComponentName componentName, boolean newState) {
        try {
+1 −0
Original line number Diff line number Diff line
@@ -425,6 +425,7 @@ interface IPackageManager {
    /** Themes */
    void updateIconMapping(String pkgName);
    ComposedIconInfo getComposedIconInfo();
    int processThemeResources(String themePkgName);

    /** Protected Apps */
    void setComponentProtectedSetting(in ComponentName componentName, in boolean newState,
+12 −0
Original line number Diff line number Diff line
@@ -3290,6 +3290,18 @@ public abstract class PackageManager {
     */
    public abstract void updateIconMaps(String pkgName);

    /**
     * Used to compile theme resources for a given theme
     * @param themePkgName
     * @return A value of 0 indicates success.  Possible errors returned are:
     * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR},
     * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR}, or
     * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR}
     *
     * @hide
     */
    public abstract int processThemeResources(String themePkgName);

    /**
     * Update Component protection state
     * @hide
+54 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package android.content.pm;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -22,7 +23,11 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.IntentFilter;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.ThemeConfig;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.FileUtils;
@@ -103,6 +108,10 @@ public class ThemeUtils {

    public static final String CATEGORY_THEME_COMPONENT_PREFIX = "org.cyanogenmod.intent.category.";

    private static final String SETTINGS_DB =
            "/data/data/com.android.providers.settings/databases/settings.db";
    private static final String SETTINGS_SECURE_TABLE = "secure";

    // Actions in manifests which identify legacy icon packs
    public static final String[] sSupportedActions = new String[] {
            "org.adw.launcher.THEMES",
@@ -668,4 +677,49 @@ public class ThemeUtils {
            }
        }
    }

    /**
     * Get the boot theme by accessing the settings.db directly instead of using a content resolver.
     * Only use this when the system is starting up and the settings content provider is not ready.
     *
     * Note: This method will only succeed if the system is calling this since normal apps will not
     * be able to access the settings db path.
     *
     * @return The boot theme or null if unable to read the database or get the entry for theme
     *         config
     */
    public static ThemeConfig getBootThemeDirty() {
        ThemeConfig config = null;
        SQLiteDatabase db = null;
        try {
            db = SQLiteDatabase.openDatabase(SETTINGS_DB, null,
                    SQLiteDatabase.OPEN_READONLY);
            if (db != null) {
                String selection = "name=?";
                String[] selectionArgs =
                        { Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY };
                String[] columns = {"value"};
                Cursor c = db.query(SETTINGS_SECURE_TABLE, columns, selection, selectionArgs,
                        null, null, null);
                if (c != null) {
                    if (c.getCount() > 0) {
                        c.moveToFirst();
                        String json = c.getString(0);
                        if (json != null) {
                            config = ThemeConfig.fromJson(json);
                        }
                    }
                    c.close();
                }
            }
        } catch (Exception e) {
            Log.w(TAG, "Unable to open " + SETTINGS_DB, e);
        } finally {
            if (db != null) {
                db.close();
            }
        }

        return config;
    }
}
+55 −0
Original line number Diff line number Diff line
@@ -87,6 +87,8 @@ import java.util.List;
public class ThemeService extends IThemeService.Stub {
    private static final String TAG = ThemeService.class.getName();

    private static final boolean DEBUG = false;

    private static final String GOOGLE_SETUPWIZARD_PACKAGE = "com.google.android.setupwizard";
    private static final String CM_SETUPWIZARD_PACKAGE = "com.cyanogenmod.account";

@@ -105,10 +107,14 @@ public class ThemeService extends IThemeService.Stub {
    private final RemoteCallbackList<IThemeChangeListener> mClients =
            new RemoteCallbackList<IThemeChangeListener>();

    final private ArrayList<String> mThemesToProcessQueue = new ArrayList<String>(0);

    private class ThemeWorkerHandler extends Handler {
        private static final int MESSAGE_CHANGE_THEME = 1;
        private static final int MESSAGE_APPLY_DEFAULT_THEME = 2;
        private static final int MESSAGE_BUILD_ICON_CACHE = 3;
        private static final int MESSAGE_QUEUE_THEME_FOR_PROCESSING = 4;
        private static final int MESSAGE_DEQUEUE_AND_PROCESS_THEME = 5;

        public ThemeWorkerHandler(Looper looper) {
            super(looper);
@@ -127,6 +133,33 @@ public class ThemeService extends IThemeService.Stub {
                case MESSAGE_BUILD_ICON_CACHE:
                    doBuildIconCache();
                    break;
                case MESSAGE_QUEUE_THEME_FOR_PROCESSING:
                    String pkgName = (String) msg.obj;
                    synchronized (mThemesToProcessQueue) {
                        if (!mThemesToProcessQueue.contains(pkgName)) {
                            if (DEBUG) Log.d(TAG, "Adding " + pkgName + " for processing");
                            mThemesToProcessQueue.add(pkgName);
                            if (mThemesToProcessQueue.size() == 1) {
                                this.sendEmptyMessage(MESSAGE_DEQUEUE_AND_PROCESS_THEME);
                            }
                        }
                    }
                    break;
                case MESSAGE_DEQUEUE_AND_PROCESS_THEME:
                    synchronized (mThemesToProcessQueue) {
                        pkgName = mThemesToProcessQueue.get(0);
                    }
                    if (pkgName != null) {
                        if (DEBUG) Log.d(TAG, "Processing " + pkgName);
                        mContext.getPackageManager().processThemeResources(pkgName);
                        synchronized (mThemesToProcessQueue) {
                            mThemesToProcessQueue.remove(0);
                            if (mThemesToProcessQueue.size() > 0) {
                                this.sendEmptyMessage(MESSAGE_DEQUEUE_AND_PROCESS_THEME);
                            }
                        }
                    }
                    break;
                default:
                    Log.w(TAG, "Unknown message " + msg.what);
                    break;
@@ -155,6 +188,7 @@ public class ThemeService extends IThemeService.Stub {
        // listen for wallpaper changes
        IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
        mContext.registerReceiver(mWallpaperChangeReceiver, filter);
        processInstalledThemes();
    }

    private void doApplyTheme(Map<String, String> componentMap) {
@@ -935,4 +969,25 @@ public class ThemeService extends IThemeService.Stub {
            }
        }
    }

    private void processInstalledThemes() {
        final String defaultTheme = ThemeUtils.getDefaultThemePackageName(mContext);
        Message msg;
        // Make sure the default theme is the first to get processed!
        if (!ThemeConfig.HOLO_DEFAULT.equals(defaultTheme)) {
            msg = mHandler.obtainMessage(ThemeWorkerHandler.MESSAGE_QUEUE_THEME_FOR_PROCESSING,
                    0, 0, defaultTheme);
            mHandler.sendMessage(msg);
        }
        // Iterate over all installed packages and queue up the ones that are themes or icon packs
        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackages(0);
        for (PackageInfo info : packages) {
            if (!defaultTheme.equals(info.packageName) &&
                    (info.isThemeApk || info.isLegacyThemeApk || info.isLegacyIconPackApk)) {
                msg = mHandler.obtainMessage(ThemeWorkerHandler.MESSAGE_QUEUE_THEME_FOR_PROCESSING,
                        0, 0, info.packageName);
                mHandler.sendMessage(msg);
            }
        }
    }
}
Loading