Home | History | Annotate | Download | only in am
      1 package com.android.server.am;
      2 
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.FileOutputStream;
      6 import java.util.HashMap;
      7 import java.util.Iterator;
      8 import java.util.Map;
      9 
     10 import org.xmlpull.v1.XmlPullParser;
     11 import org.xmlpull.v1.XmlPullParserException;
     12 import org.xmlpull.v1.XmlSerializer;
     13 
     14 import com.android.internal.util.FastXmlSerializer;
     15 
     16 import android.app.ActivityManager;
     17 import android.app.AppGlobals;
     18 import android.content.pm.ActivityInfo;
     19 import android.content.pm.ApplicationInfo;
     20 import android.content.pm.IPackageManager;
     21 import android.content.res.CompatibilityInfo;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.os.RemoteException;
     25 import android.util.AtomicFile;
     26 import android.util.Slog;
     27 import android.util.Xml;
     28 
     29 public class CompatModePackages {
     30     private final String TAG = ActivityManagerService.TAG;
     31     private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
     32 
     33     private final ActivityManagerService mService;
     34     private final AtomicFile mFile;
     35 
     36     // Compatibility state: no longer ask user to select the mode.
     37     public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
     38     // Compatibility state: compatibility mode is enabled.
     39     public static final int COMPAT_FLAG_ENABLED = 1<<1;
     40 
     41     private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
     42 
     43     private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
     44 
     45     private final Handler mHandler = new Handler() {
     46         @Override public void handleMessage(Message msg) {
     47             switch (msg.what) {
     48                 case MSG_WRITE:
     49                     saveCompatModes();
     50                     break;
     51                 default:
     52                     super.handleMessage(msg);
     53                     break;
     54             }
     55         }
     56     };
     57 
     58     public CompatModePackages(ActivityManagerService service, File systemDir) {
     59         mService = service;
     60         mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"));
     61 
     62         FileInputStream fis = null;
     63         try {
     64             fis = mFile.openRead();
     65             XmlPullParser parser = Xml.newPullParser();
     66             parser.setInput(fis, null);
     67             int eventType = parser.getEventType();
     68             while (eventType != XmlPullParser.START_TAG) {
     69                 eventType = parser.next();
     70             }
     71             String tagName = parser.getName();
     72             if ("compat-packages".equals(tagName)) {
     73                 eventType = parser.next();
     74                 do {
     75                     if (eventType == XmlPullParser.START_TAG) {
     76                         tagName = parser.getName();
     77                         if (parser.getDepth() == 2) {
     78                             if ("pkg".equals(tagName)) {
     79                                 String pkg = parser.getAttributeValue(null, "name");
     80                                 if (pkg != null) {
     81                                     String mode = parser.getAttributeValue(null, "mode");
     82                                     int modeInt = 0;
     83                                     if (mode != null) {
     84                                         try {
     85                                             modeInt = Integer.parseInt(mode);
     86                                         } catch (NumberFormatException e) {
     87                                         }
     88                                     }
     89                                     mPackages.put(pkg, modeInt);
     90                                 }
     91                             }
     92                         }
     93                     }
     94                     eventType = parser.next();
     95                 } while (eventType != XmlPullParser.END_DOCUMENT);
     96             }
     97         } catch (XmlPullParserException e) {
     98             Slog.w(TAG, "Error reading compat-packages", e);
     99         } catch (java.io.IOException e) {
    100             if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
    101         } finally {
    102             if (fis != null) {
    103                 try {
    104                     fis.close();
    105                 } catch (java.io.IOException e1) {
    106                 }
    107             }
    108         }
    109     }
    110 
    111     public HashMap<String, Integer> getPackages() {
    112         return mPackages;
    113     }
    114 
    115     private int getPackageFlags(String packageName) {
    116         Integer flags = mPackages.get(packageName);
    117         return flags != null ? flags : 0;
    118     }
    119 
    120     public void handlePackageAddedLocked(String packageName, boolean updated) {
    121         ApplicationInfo ai = null;
    122         try {
    123             ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
    124         } catch (RemoteException e) {
    125         }
    126         if (ai == null) {
    127             return;
    128         }
    129         CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
    130         final boolean mayCompat = !ci.alwaysSupportsScreen()
    131                 && !ci.neverSupportsScreen();
    132 
    133         if (updated) {
    134             // Update -- if the app no longer can run in compat mode, clear
    135             // any current settings for it.
    136             if (!mayCompat && mPackages.containsKey(packageName)) {
    137                 mPackages.remove(packageName);
    138                 mHandler.removeMessages(MSG_WRITE);
    139                 Message msg = mHandler.obtainMessage(MSG_WRITE);
    140                 mHandler.sendMessageDelayed(msg, 10000);
    141             }
    142         }
    143     }
    144 
    145     public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
    146         CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
    147                 mService.mConfiguration.smallestScreenWidthDp,
    148                 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
    149         //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
    150         return ci;
    151     }
    152 
    153     public int computeCompatModeLocked(ApplicationInfo ai) {
    154         boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
    155         CompatibilityInfo info = new CompatibilityInfo(ai,
    156                 mService.mConfiguration.screenLayout,
    157                 mService.mConfiguration.smallestScreenWidthDp, enabled);
    158         if (info.alwaysSupportsScreen()) {
    159             return ActivityManager.COMPAT_MODE_NEVER;
    160         }
    161         if (info.neverSupportsScreen()) {
    162             return ActivityManager.COMPAT_MODE_ALWAYS;
    163         }
    164         return enabled ? ActivityManager.COMPAT_MODE_ENABLED
    165                 : ActivityManager.COMPAT_MODE_DISABLED;
    166     }
    167 
    168     public boolean getFrontActivityAskCompatModeLocked() {
    169         ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
    170         if (r == null) {
    171             return false;
    172         }
    173         return getPackageAskCompatModeLocked(r.packageName);
    174     }
    175 
    176     public boolean getPackageAskCompatModeLocked(String packageName) {
    177         return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
    178     }
    179 
    180     public void setFrontActivityAskCompatModeLocked(boolean ask) {
    181         ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
    182         if (r != null) {
    183             setPackageAskCompatModeLocked(r.packageName, ask);
    184         }
    185     }
    186 
    187     public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
    188         int curFlags = getPackageFlags(packageName);
    189         int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
    190         if (curFlags != newFlags) {
    191             if (newFlags != 0) {
    192                 mPackages.put(packageName, newFlags);
    193             } else {
    194                 mPackages.remove(packageName);
    195             }
    196             mHandler.removeMessages(MSG_WRITE);
    197             Message msg = mHandler.obtainMessage(MSG_WRITE);
    198             mHandler.sendMessageDelayed(msg, 10000);
    199         }
    200     }
    201 
    202     public int getFrontActivityScreenCompatModeLocked() {
    203         ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
    204         if (r == null) {
    205             return ActivityManager.COMPAT_MODE_UNKNOWN;
    206         }
    207         return computeCompatModeLocked(r.info.applicationInfo);
    208     }
    209 
    210     public void setFrontActivityScreenCompatModeLocked(int mode) {
    211         ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
    212         if (r == null) {
    213             Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
    214             return;
    215         }
    216         setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
    217     }
    218 
    219     public int getPackageScreenCompatModeLocked(String packageName) {
    220         ApplicationInfo ai = null;
    221         try {
    222             ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
    223         } catch (RemoteException e) {
    224         }
    225         if (ai == null) {
    226             return ActivityManager.COMPAT_MODE_UNKNOWN;
    227         }
    228         return computeCompatModeLocked(ai);
    229     }
    230 
    231     public void setPackageScreenCompatModeLocked(String packageName, int mode) {
    232         ApplicationInfo ai = null;
    233         try {
    234             ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
    235         } catch (RemoteException e) {
    236         }
    237         if (ai == null) {
    238             Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
    239             return;
    240         }
    241         setPackageScreenCompatModeLocked(ai, mode);
    242     }
    243 
    244     private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
    245         final String packageName = ai.packageName;
    246 
    247         int curFlags = getPackageFlags(packageName);
    248 
    249         boolean enable;
    250         switch (mode) {
    251             case ActivityManager.COMPAT_MODE_DISABLED:
    252                 enable = false;
    253                 break;
    254             case ActivityManager.COMPAT_MODE_ENABLED:
    255                 enable = true;
    256                 break;
    257             case ActivityManager.COMPAT_MODE_TOGGLE:
    258                 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
    259                 break;
    260             default:
    261                 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
    262                 return;
    263         }
    264 
    265         int newFlags = curFlags;
    266         if (enable) {
    267             newFlags |= COMPAT_FLAG_ENABLED;
    268         } else {
    269             newFlags &= ~COMPAT_FLAG_ENABLED;
    270         }
    271 
    272         CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
    273         if (ci.alwaysSupportsScreen()) {
    274             Slog.w(TAG, "Ignoring compat mode change of " + packageName
    275                     + "; compatibility never needed");
    276             newFlags = 0;
    277         }
    278         if (ci.neverSupportsScreen()) {
    279             Slog.w(TAG, "Ignoring compat mode change of " + packageName
    280                     + "; compatibility always needed");
    281             newFlags = 0;
    282         }
    283 
    284         if (newFlags != curFlags) {
    285             if (newFlags != 0) {
    286                 mPackages.put(packageName, newFlags);
    287             } else {
    288                 mPackages.remove(packageName);
    289             }
    290 
    291             // Need to get compatibility info in new state.
    292             ci = compatibilityInfoForPackageLocked(ai);
    293 
    294             mHandler.removeMessages(MSG_WRITE);
    295             Message msg = mHandler.obtainMessage(MSG_WRITE);
    296             mHandler.sendMessageDelayed(msg, 10000);
    297 
    298             ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
    299 
    300             // All activities that came from the package must be
    301             // restarted as if there was a config change.
    302             for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
    303                 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
    304                 if (a.info.packageName.equals(packageName)) {
    305                     a.forceNewConfig = true;
    306                     if (starting != null && a == starting && a.visible) {
    307                         a.startFreezingScreenLocked(starting.app,
    308                                 ActivityInfo.CONFIG_SCREEN_LAYOUT);
    309                     }
    310                 }
    311             }
    312 
    313             // Tell all processes that loaded this package about the change.
    314             for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
    315                 ProcessRecord app = mService.mLruProcesses.get(i);
    316                 if (!app.pkgList.contains(packageName)) {
    317                     continue;
    318                 }
    319                 try {
    320                     if (app.thread != null) {
    321                         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
    322                                 + app.processName + " new compat " + ci);
    323                         app.thread.updatePackageCompatibilityInfo(packageName, ci);
    324                     }
    325                 } catch (Exception e) {
    326                 }
    327             }
    328 
    329             if (starting != null) {
    330                 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
    331                 // And we need to make sure at this point that all other activities
    332                 // are made visible with the correct configuration.
    333                 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
    334             }
    335         }
    336     }
    337 
    338     void saveCompatModes() {
    339         HashMap<String, Integer> pkgs;
    340         synchronized (mService) {
    341             pkgs = new HashMap<String, Integer>(mPackages);
    342         }
    343 
    344         FileOutputStream fos = null;
    345 
    346         try {
    347             fos = mFile.startWrite();
    348             XmlSerializer out = new FastXmlSerializer();
    349             out.setOutput(fos, "utf-8");
    350             out.startDocument(null, true);
    351             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    352             out.startTag(null, "compat-packages");
    353 
    354             final IPackageManager pm = AppGlobals.getPackageManager();
    355             final int screenLayout = mService.mConfiguration.screenLayout;
    356             final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
    357             final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
    358             while (it.hasNext()) {
    359                 Map.Entry<String, Integer> entry = it.next();
    360                 String pkg = entry.getKey();
    361                 int mode = entry.getValue();
    362                 if (mode == 0) {
    363                     continue;
    364                 }
    365                 ApplicationInfo ai = null;
    366                 try {
    367                     ai = pm.getApplicationInfo(pkg, 0, 0);
    368                 } catch (RemoteException e) {
    369                 }
    370                 if (ai == null) {
    371                     continue;
    372                 }
    373                 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
    374                         smallestScreenWidthDp, false);
    375                 if (info.alwaysSupportsScreen()) {
    376                     continue;
    377                 }
    378                 if (info.neverSupportsScreen()) {
    379                     continue;
    380                 }
    381                 out.startTag(null, "pkg");
    382                 out.attribute(null, "name", pkg);
    383                 out.attribute(null, "mode", Integer.toString(mode));
    384                 out.endTag(null, "pkg");
    385             }
    386 
    387             out.endTag(null, "compat-packages");
    388             out.endDocument();
    389 
    390             mFile.finishWrite(fos);
    391         } catch (java.io.IOException e1) {
    392             Slog.w(TAG, "Error writing compat packages", e1);
    393             if (fos != null) {
    394                 mFile.failWrite(fos);
    395             }
    396         }
    397     }
    398 }
    399