diff options
Diffstat (limited to 'services/core/java/com/android/server/am/CompatModePackages.java')
-rw-r--r-- | services/core/java/com/android/server/am/CompatModePackages.java | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java new file mode 100644 index 0000000..59e6787 --- /dev/null +++ b/services/core/java/com/android/server/am/CompatModePackages.java @@ -0,0 +1,385 @@ +package com.android.server.am; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import com.android.internal.util.FastXmlSerializer; + +import android.app.ActivityManager; +import android.app.AppGlobals; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.res.CompatibilityInfo; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +public final class CompatModePackages { + private final String TAG = ActivityManagerService.TAG; + private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; + + private final ActivityManagerService mService; + private final AtomicFile mFile; + + // Compatibility state: no longer ask user to select the mode. + public static final int COMPAT_FLAG_DONT_ASK = 1<<0; + // Compatibility state: compatibility mode is enabled. + public static final int COMPAT_FLAG_ENABLED = 1<<1; + + private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); + + private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG; + + private final Handler mHandler = new Handler() { + @Override public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_WRITE: + saveCompatModes(); + break; + default: + super.handleMessage(msg); + break; + } + } + }; + + public CompatModePackages(ActivityManagerService service, File systemDir) { + mService = service; + mFile = new AtomicFile(new File(systemDir, "packages-compat.xml")); + + FileInputStream fis = null; + try { + fis = mFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.START_TAG) { + eventType = parser.next(); + } + String tagName = parser.getName(); + if ("compat-packages".equals(tagName)) { + eventType = parser.next(); + do { + if (eventType == XmlPullParser.START_TAG) { + tagName = parser.getName(); + if (parser.getDepth() == 2) { + if ("pkg".equals(tagName)) { + String pkg = parser.getAttributeValue(null, "name"); + if (pkg != null) { + String mode = parser.getAttributeValue(null, "mode"); + int modeInt = 0; + if (mode != null) { + try { + modeInt = Integer.parseInt(mode); + } catch (NumberFormatException e) { + } + } + mPackages.put(pkg, modeInt); + } + } + } + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + } + } catch (XmlPullParserException e) { + Slog.w(TAG, "Error reading compat-packages", e); + } catch (java.io.IOException e) { + if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (java.io.IOException e1) { + } + } + } + } + + public HashMap<String, Integer> getPackages() { + return mPackages; + } + + private int getPackageFlags(String packageName) { + Integer flags = mPackages.get(packageName); + return flags != null ? flags : 0; + } + + public void handlePackageAddedLocked(String packageName, boolean updated) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); + } catch (RemoteException e) { + } + if (ai == null) { + return; + } + CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); + final boolean mayCompat = !ci.alwaysSupportsScreen() + && !ci.neverSupportsScreen(); + + if (updated) { + // 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) { + CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout, + mService.mConfiguration.smallestScreenWidthDp, + (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0); + //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci); + return ci; + } + + public int computeCompatModeLocked(ApplicationInfo ai) { + boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0; + CompatibilityInfo info = new CompatibilityInfo(ai, + mService.mConfiguration.screenLayout, + mService.mConfiguration.smallestScreenWidthDp, enabled); + if (info.alwaysSupportsScreen()) { + return ActivityManager.COMPAT_MODE_NEVER; + } + if (info.neverSupportsScreen()) { + return ActivityManager.COMPAT_MODE_ALWAYS; + } + return enabled ? ActivityManager.COMPAT_MODE_ENABLED + : ActivityManager.COMPAT_MODE_DISABLED; + } + + public boolean getFrontActivityAskCompatModeLocked() { + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + if (r == null) { + return false; + } + return getPackageAskCompatModeLocked(r.packageName); + } + + public boolean getPackageAskCompatModeLocked(String packageName) { + return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; + } + + public void setFrontActivityAskCompatModeLocked(boolean ask) { + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + if (r != null) { + setPackageAskCompatModeLocked(r.packageName, ask); + } + } + + public void setPackageAskCompatModeLocked(String packageName, boolean ask) { + int curFlags = getPackageFlags(packageName); + int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK); + if (curFlags != newFlags) { + if (newFlags != 0) { + mPackages.put(packageName, newFlags); + } else { + mPackages.remove(packageName); + } + mHandler.removeMessages(MSG_WRITE); + Message msg = mHandler.obtainMessage(MSG_WRITE); + mHandler.sendMessageDelayed(msg, 10000); + } + } + + public int getFrontActivityScreenCompatModeLocked() { + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + if (r == null) { + return ActivityManager.COMPAT_MODE_UNKNOWN; + } + return computeCompatModeLocked(r.info.applicationInfo); + } + + public void setFrontActivityScreenCompatModeLocked(int mode) { + ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null); + if (r == null) { + Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); + return; + } + setPackageScreenCompatModeLocked(r.info.applicationInfo, mode); + } + + public int getPackageScreenCompatModeLocked(String packageName) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); + } catch (RemoteException e) { + } + if (ai == null) { + return ActivityManager.COMPAT_MODE_UNKNOWN; + } + return computeCompatModeLocked(ai); + } + + public void setPackageScreenCompatModeLocked(String packageName, int mode) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); + } catch (RemoteException e) { + } + if (ai == null) { + Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); + return; + } + setPackageScreenCompatModeLocked(ai, mode); + } + + private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { + final String packageName = ai.packageName; + + int curFlags = getPackageFlags(packageName); + + boolean enable; + switch (mode) { + case ActivityManager.COMPAT_MODE_DISABLED: + enable = false; + break; + case ActivityManager.COMPAT_MODE_ENABLED: + enable = true; + break; + case ActivityManager.COMPAT_MODE_TOGGLE: + enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; + break; + default: + Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); + return; + } + + int newFlags = curFlags; + if (enable) { + newFlags |= COMPAT_FLAG_ENABLED; + } else { + newFlags &= ~COMPAT_FLAG_ENABLED; + } + + CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); + if (ci.alwaysSupportsScreen()) { + Slog.w(TAG, "Ignoring compat mode change of " + packageName + + "; compatibility never needed"); + newFlags = 0; + } + if (ci.neverSupportsScreen()) { + Slog.w(TAG, "Ignoring compat mode change of " + packageName + + "; compatibility always needed"); + 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); + + final ActivityStack stack = mService.getFocusedStack(); + ActivityRecord starting = stack.restartPackage(packageName); + + // 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); + if (!app.pkgList.containsKey(packageName)) { + continue; + } + try { + if (app.thread != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + + app.processName + " new compat " + ci); + app.thread.updatePackageCompatibilityInfo(packageName, ci); + } + } catch (Exception e) { + } + } + + if (starting != null) { + stack.ensureActivityConfigurationLocked(starting, 0); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + stack.ensureActivitiesVisibleLocked(starting, 0); + } + } + } + + void saveCompatModes() { + HashMap<String, Integer> pkgs; + synchronized (mService) { + pkgs = new HashMap<String, Integer>(mPackages); + } + + FileOutputStream fos = null; + + try { + fos = mFile.startWrite(); + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(fos, "utf-8"); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + out.startTag(null, "compat-packages"); + + final IPackageManager pm = AppGlobals.getPackageManager(); + final int screenLayout = mService.mConfiguration.screenLayout; + final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp; + final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<String, Integer> entry = it.next(); + String pkg = entry.getKey(); + int mode = entry.getValue(); + if (mode == 0) { + continue; + } + ApplicationInfo ai = null; + try { + ai = pm.getApplicationInfo(pkg, 0, 0); + } catch (RemoteException e) { + } + if (ai == null) { + continue; + } + CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, + smallestScreenWidthDp, false); + if (info.alwaysSupportsScreen()) { + continue; + } + if (info.neverSupportsScreen()) { + continue; + } + out.startTag(null, "pkg"); + out.attribute(null, "name", pkg); + out.attribute(null, "mode", Integer.toString(mode)); + out.endTag(null, "pkg"); + } + + out.endTag(null, "compat-packages"); + out.endDocument(); + + mFile.finishWrite(fos); + } catch (java.io.IOException e1) { + Slog.w(TAG, "Error writing compat packages", e1); + if (fos != null) { + mFile.failWrite(fos); + } + } + } +} |