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