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