• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CompatModePackages(ActivityManagerService service, File systemDir)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 
getPackages()111     public HashMap<String, Integer> getPackages() {
112         return mPackages;
113     }
114 
getPackageFlags(String packageName)115     private int getPackageFlags(String packageName) {
116         Integer flags = mPackages.get(packageName);
117         return flags != null ? flags : 0;
118     }
119 
handlePackageAddedLocked(String packageName, boolean updated)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 
compatibilityInfoForPackageLocked(ApplicationInfo ai)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 
computeCompatModeLocked(ApplicationInfo ai)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 
getFrontActivityAskCompatModeLocked()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 
getPackageAskCompatModeLocked(String packageName)176     public boolean getPackageAskCompatModeLocked(String packageName) {
177         return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
178     }
179 
setFrontActivityAskCompatModeLocked(boolean ask)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 
setPackageAskCompatModeLocked(String packageName, boolean ask)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 
getFrontActivityScreenCompatModeLocked()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 
setFrontActivityScreenCompatModeLocked(int mode)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 
getPackageScreenCompatModeLocked(String packageName)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 
setPackageScreenCompatModeLocked(String packageName, int mode)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 
setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode)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 
saveCompatModes()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