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

Commit 8ea5e1d7 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix compat mode bugs when updating apps.

No longer accidentally puts an app into compatibility mode.

Also various cleanup, freezing screen while switching between modes.

Change-Id: Ic1b3958be7800189a93f68e9dee3c5adfc45fe57
parent b96cbbd1
Loading
Loading
Loading
Loading
+65 −53
Original line number Diff line number Diff line
@@ -62,38 +62,20 @@ public class CompatibilityInfo implements Parcelable {
     */
    private static final int SCALING_REQUIRED = 1; 

    /**
     * Has the application said that its UI is expandable?  Based on the
     * <supports-screen> android:expandible in the manifest.
     */
    private static final int EXPANDABLE = 2;
    
    /**
     * Has the application said that its UI supports large screens?  Based on the
     * <supports-screen> android:largeScreens in the manifest.
     */
    private static final int LARGE_SCREENS = 8;
    
    /**
     * Has the application said that its UI supports xlarge screens?  Based on the
     * <supports-screen> android:xlargeScreens in the manifest.
     */
    private static final int XLARGE_SCREENS = 32;
    
    /**
     * Application must always run in compatibility mode?
     */
    private static final int ALWAYS_COMPAT = 64;
    private static final int ALWAYS_NEEDS_COMPAT = 2;

    /**
     * Application never should run in compatibility mode?
     */
    private static final int NEVER_COMPAT = 128;
    private static final int NEVER_NEEDS_COMPAT = 4;

    /**
     * Set if the application needs to run in screen size compatibility mode.
     */
    private static final int NEEDS_SCREEN_COMPAT = 256;
    private static final int NEEDS_SCREEN_COMPAT = 8;

    /**
     * The effective screen density we have selected for this application.
@@ -127,7 +109,7 @@ public class CompatibilityInfo implements Parcelable {
            }

            if (compat >= sw) {
                compatFlags |= NEVER_COMPAT;
                compatFlags |= NEVER_NEEDS_COMPAT;
            } else if (forceCompat) {
                compatFlags |= NEEDS_SCREEN_COMPAT;
            }
@@ -138,29 +120,49 @@ public class CompatibilityInfo implements Parcelable {
            applicationInvertedScale = 1.0f;

        } else {
            /**
             * Has the application said that its UI is expandable?  Based on the
             * <supports-screen> android:expandible in the manifest.
             */
            final int EXPANDABLE = 2;

            /**
             * Has the application said that its UI supports large screens?  Based on the
             * <supports-screen> android:largeScreens in the manifest.
             */
            final int LARGE_SCREENS = 8;

            /**
             * Has the application said that its UI supports xlarge screens?  Based on the
             * <supports-screen> android:xlargeScreens in the manifest.
             */
            final int XLARGE_SCREENS = 32;

            int sizeInfo = 0;

            // We can't rely on the application always setting
            // FLAG_RESIZEABLE_FOR_SCREENS so will compute it based on various input.
            boolean anyResizeable = false;

            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
                compatFlags |= LARGE_SCREENS;
                sizeInfo |= LARGE_SCREENS;
                anyResizeable = true;
                if (!forceCompat) {
                    // If we aren't forcing the app into compatibility mode, then
                    // assume if it supports large screens that we should allow it
                    // to use the full space of an xlarge screen as well.
                    compatFlags |= XLARGE_SCREENS | EXPANDABLE;
                    sizeInfo |= XLARGE_SCREENS | EXPANDABLE;
                }
            }
            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
                anyResizeable = true;
                if (!forceCompat) {
                    compatFlags |= XLARGE_SCREENS | EXPANDABLE;
                    sizeInfo |= XLARGE_SCREENS | EXPANDABLE;
                }
            }
            if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
                anyResizeable = true;
                compatFlags |= EXPANDABLE;
                sizeInfo |= EXPANDABLE;
            }

            if (forceCompat) {
@@ -168,43 +170,37 @@ public class CompatibilityInfo implements Parcelable {
                // just says it is resizable for screens.  We'll only have it fill
                // the screen if it explicitly says it supports the screen size we
                // are running in.
                compatFlags &= ~EXPANDABLE;
                sizeInfo &= ~EXPANDABLE;
            }

            boolean supportsScreen = false;
            compatFlags |= NEEDS_SCREEN_COMPAT;
            switch (screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) {
                case Configuration.SCREENLAYOUT_SIZE_XLARGE:
                    if ((compatFlags&XLARGE_SCREENS) != 0) {
                        supportsScreen = true;
                    if ((sizeInfo&XLARGE_SCREENS) != 0) {
                        compatFlags &= ~NEEDS_SCREEN_COMPAT;
                    }
                    if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
                        compatFlags |= NEVER_COMPAT;
                        compatFlags |= NEVER_NEEDS_COMPAT;
                    }
                    break;
                case Configuration.SCREENLAYOUT_SIZE_LARGE:
                    if ((compatFlags&LARGE_SCREENS) != 0) {
                        supportsScreen = true;
                    if ((sizeInfo&LARGE_SCREENS) != 0) {
                        compatFlags &= ~NEEDS_SCREEN_COMPAT;
                    }
                    if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
                        compatFlags |= NEVER_COMPAT;
                        compatFlags |= NEVER_NEEDS_COMPAT;
                    }
                    break;
            }

            if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) != 0) {
                if ((compatFlags&EXPANDABLE) != 0) {
                    supportsScreen = true;
                if ((sizeInfo&EXPANDABLE) != 0) {
                    compatFlags &= ~NEEDS_SCREEN_COMPAT;
                } else if (!anyResizeable) {
                    compatFlags |= ALWAYS_COMPAT;
                    compatFlags |= ALWAYS_NEEDS_COMPAT;
                }
            }

            if (supportsScreen) {
                compatFlags &= ~NEEDS_SCREEN_COMPAT;
            } else {
                compatFlags |= NEEDS_SCREEN_COMPAT;
            }

            if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
                applicationDensity = DisplayMetrics.DENSITY_DEVICE;
                applicationScale = 1.0f;
@@ -230,8 +226,7 @@ public class CompatibilityInfo implements Parcelable {
    }

    private CompatibilityInfo() {
        this(XLARGE_SCREENS | LARGE_SCREENS | EXPANDABLE,
                DisplayMetrics.DENSITY_DEVICE,
        this(NEVER_NEEDS_COMPAT, DisplayMetrics.DENSITY_DEVICE,
                1.0f,
                1.0f);
    }
@@ -248,16 +243,11 @@ public class CompatibilityInfo implements Parcelable {
    }
    
    public boolean neverSupportsScreen() {
        return (mCompatibilityFlags&NEVER_COMPAT) != 0;
        return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
    }

    public boolean alwaysSupportsScreen() {
        return (mCompatibilityFlags&ALWAYS_COMPAT) != 0;
    }

    @Override
    public String toString() {
        return "CompatibilityInfo{scale=" + applicationScale + "}";
        return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
    }

    /**
@@ -515,6 +505,28 @@ public class CompatibilityInfo implements Parcelable {
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(128);
        sb.append("{");
        sb.append(applicationDensity);
        sb.append("dpi");
        if (isScalingRequired()) {
            sb.append(" scaling");
        }
        if (!supportsScreen()) {
            sb.append(" resizing");
        }
        if (neverSupportsScreen()) {
            sb.append(" never-compat");
        }
        if (alwaysSupportsScreen()) {
            sb.append(" always-compat");
        }
        sb.append("}");
        return sb.toString();
    }

    @Override
    public int hashCode() {
        int result = 17;
+7 −9
Original line number Diff line number Diff line
@@ -3642,12 +3642,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                    + processName + " with config " + mConfiguration);
            ApplicationInfo appInfo = app.instrumentationInfo != null
                    ? app.instrumentationInfo : app.info;
            app.compat = compatibilityInfoForPackageLocked(appInfo);
            thread.bindApplication(processName, appInfo, providers,
                    app.instrumentationClass, app.instrumentationProfileFile,
                    app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                    isRestrictedBackupMode || !normalMode,
                    mConfiguration, compatibilityInfoForPackageLocked(appInfo),
                    getCommonServicesLocked(),
                    mConfiguration, app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, true);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
@@ -10977,13 +10977,11 @@ public final class ActivityManagerService extends ActivityManagerNative
        // Special case for adding a package: by default turn on compatibility
        // mode.
        } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
            if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
            Uri data = intent.getData();
            String ssp;
            if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                    mCompatModePackages.setPackageScreenCompatModeLocked(ssp,
                            ActivityManager.COMPAT_MODE_ENABLED);
                }
                mCompatModePackages.handlePackageAddedLocked(ssp,
                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false));
            }
        }
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
@@ -78,6 +79,7 @@ class ActivityRecord extends IApplicationToken.Stub {
    long startTime;         // last time this activity was started
    long cpuTimeAtResume;   // the cpu time of host process at the time of resuming activity
    Configuration configuration; // configuration activity was last running in
    CompatibilityInfo compat;// last used compatibility mode
    ActivityRecord resultTo; // who started this entry, so will get our reply
    final String resultWho; // additional identifier for use by resultTo.
    final int requestCode;  // code given by requester (resultTo)
@@ -137,6 +139,7 @@ class ActivityRecord extends IApplicationToken.Stub {
                pw.print(" componentSpecified="); pw.print(componentSpecified);
                pw.print(" isHomeActivity="); pw.println(isHomeActivity);
        pw.print(prefix); pw.print("config="); pw.println(configuration);
        pw.print(prefix); pw.print("compat="); pw.println(compat);
        if (resultTo != null || resultWho != null) {
            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
                    pw.print(" resultWho="); pw.print(resultWho);
+2 −2
Original line number Diff line number Diff line
@@ -546,10 +546,10 @@ public class ActivityStack {
            r.sleeping = false;
            r.forceNewConfig = false;
            showAskCompatModeDialogLocked(r);
            r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                    System.identityHashCode(r),
                    r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo),
                    r.icicle, results, newIntents, !andResume,
                    r.info, r.compat, r.icicle, results, newIntents, !andResume,
                    mService.isNextTransitionForward());
            
            if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+62 −21
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ import com.android.internal.util.FastXmlSerializer;

import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.res.CompatibilityInfo;
@@ -117,6 +118,37 @@ public class CompatModePackages {
        return flags != null ? flags : 0;
    }

    public void handlePackageAddedLocked(String packageName, boolean updated) {
        ApplicationInfo ai = null;
        try {
            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
        } catch (RemoteException e) {
        }
        if (ai == null) {
            return;
        }
        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
        final boolean mayCompat = !ci.alwaysSupportsScreen()
                && !ci.neverSupportsScreen();

        if (!updated) {
            // First time -- if the app may run in compat mode, enable that
            // by default.
            if (mayCompat) {
                setPackageScreenCompatModeLocked(ai, ActivityManager.COMPAT_MODE_ENABLED);
            }
        } else {
            // Update -- if the app no longer can run in compat mode, clear
            // any current settings for it.
            if (!mayCompat && mPackages.containsKey(packageName)) {
                mPackages.remove(packageName);
                mHandler.removeMessages(MSG_WRITE);
                Message msg = mHandler.obtainMessage(MSG_WRITE);
                mHandler.sendMessageDelayed(msg, 10000);
            }
        }
    }

    public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
        return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
                mService.mConfiguration.smallestScreenWidthDp,
@@ -242,28 +274,47 @@ public class CompatModePackages {
            newFlags &= ~COMPAT_FLAG_ENABLED;
        }

        if (newFlags != curFlags) {
            if (newFlags != 0) {
                mPackages.put(packageName, newFlags);
            } else {
                mPackages.remove(packageName);
            }
        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
        if (ci.alwaysSupportsScreen()) {
            Slog.w(TAG, "Ignoring compat mode change of " + packageName
                    + "; compatibility never needed");
                return;
            newFlags = 0;
        }
        if (ci.neverSupportsScreen()) {
            Slog.w(TAG, "Ignoring compat mode change of " + packageName
                    + "; compatibility always needed");
                return;
            newFlags = 0;
        }

        if (newFlags != curFlags) {
            if (newFlags != 0) {
                mPackages.put(packageName, newFlags);
            } else {
                mPackages.remove(packageName);
            }

            // Need to get compatibility info in new state.
            ci = compatibilityInfoForPackageLocked(ai);

            mHandler.removeMessages(MSG_WRITE);
            Message msg = mHandler.obtainMessage(MSG_WRITE);
            mHandler.sendMessageDelayed(msg, 10000);

            ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);

            // All activities that came from the package must be
            // restarted as if there was a config change.
            for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
                ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
                if (a.info.packageName.equals(packageName)) {
                    a.forceNewConfig = true;
                    if (starting != null && a == starting && a.visible) {
                        a.startFreezingScreenLocked(starting.app,
                                ActivityInfo.CONFIG_SCREEN_LAYOUT);
                    }
                }
            }

            // Tell all processes that loaded this package about the change.
            for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
                ProcessRecord app = mService.mLruProcesses.get(i);
@@ -280,16 +331,6 @@ public class CompatModePackages {
                }
            }

            // All activities that came from the packge must be
            // restarted as if there was a config change.
            for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
                ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
                if (a.info.packageName.equals(packageName)) {
                    a.forceNewConfig = true;
                }
            }

            ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
            if (starting != null) {
                mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
                // And we need to make sure at this point that all other activities
Loading