blob: 8949f48842f4759ea1363acd0e4a594cb2cf730e [file] [log] [blame]
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -07001package com.android.server.am;
2
3import java.io.File;
4import java.io.FileInputStream;
5import java.io.FileOutputStream;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -07006import java.util.HashMap;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -07007import java.util.HashSet;
8import java.util.Iterator;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -07009import java.util.Map;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070010
11import org.xmlpull.v1.XmlPullParser;
12import org.xmlpull.v1.XmlPullParserException;
13import org.xmlpull.v1.XmlSerializer;
14
15import com.android.internal.os.AtomicFile;
16import com.android.internal.util.FastXmlSerializer;
17
18import android.app.ActivityManager;
19import android.app.AppGlobals;
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -070020import android.content.pm.ActivityInfo;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070021import android.content.pm.ApplicationInfo;
22import android.content.pm.IPackageManager;
23import android.content.res.CompatibilityInfo;
24import android.os.Handler;
25import android.os.Message;
26import android.os.RemoteException;
27import android.util.Slog;
28import android.util.Xml;
29
30public 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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070037 // 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>();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070043
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) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070082 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);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070091 }
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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700112 public HashMap<String, Integer> getPackages() {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700113 return mPackages;
114 }
115
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700116 private int getPackageFlags(String packageName) {
117 Integer flags = mPackages.get(packageName);
118 return flags != null ? flags : 0;
119 }
120
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700121 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 // First time -- if the app may run in compat mode, enable that
136 // by default.
137 if (mayCompat) {
138 setPackageScreenCompatModeLocked(ai, ActivityManager.COMPAT_MODE_ENABLED);
139 }
140 } else {
141 // Update -- if the app no longer can run in compat mode, clear
142 // any current settings for it.
143 if (!mayCompat && mPackages.containsKey(packageName)) {
144 mPackages.remove(packageName);
145 mHandler.removeMessages(MSG_WRITE);
146 Message msg = mHandler.obtainMessage(MSG_WRITE);
147 mHandler.sendMessageDelayed(msg, 10000);
148 }
149 }
150 }
151
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700152 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
153 return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700154 mService.mConfiguration.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700155 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700156 }
157
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700158 public int computeCompatModeLocked(ApplicationInfo ai) {
159 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700160 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700161 mService.mConfiguration.screenLayout,
162 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700163 if (info.alwaysSupportsScreen()) {
164 return ActivityManager.COMPAT_MODE_NEVER;
165 }
166 if (info.neverSupportsScreen()) {
167 return ActivityManager.COMPAT_MODE_ALWAYS;
168 }
169 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
170 : ActivityManager.COMPAT_MODE_DISABLED;
171 }
172
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700173 public boolean getFrontActivityAskCompatModeLocked() {
174 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
175 if (r == null) {
176 return false;
177 }
178 return getPackageAskCompatModeLocked(r.packageName);
179 }
180
181 public boolean getPackageAskCompatModeLocked(String packageName) {
182 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
183 }
184
185 public void setFrontActivityAskCompatModeLocked(boolean ask) {
186 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
187 if (r != null) {
188 setPackageAskCompatModeLocked(r.packageName, ask);
189 }
190 }
191
192 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
193 int curFlags = getPackageFlags(packageName);
194 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
195 if (curFlags != newFlags) {
196 if (newFlags != 0) {
197 mPackages.put(packageName, newFlags);
198 } else {
199 mPackages.remove(packageName);
200 }
201 mHandler.removeMessages(MSG_WRITE);
202 Message msg = mHandler.obtainMessage(MSG_WRITE);
203 mHandler.sendMessageDelayed(msg, 10000);
204 }
205 }
206
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700207 public int getFrontActivityScreenCompatModeLocked() {
208 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
209 if (r == null) {
210 return ActivityManager.COMPAT_MODE_UNKNOWN;
211 }
212 return computeCompatModeLocked(r.info.applicationInfo);
213 }
214
215 public void setFrontActivityScreenCompatModeLocked(int mode) {
216 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
217 if (r == null) {
218 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
219 return;
220 }
221 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
222 }
223
224 public int getPackageScreenCompatModeLocked(String packageName) {
225 ApplicationInfo ai = null;
226 try {
227 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
228 } catch (RemoteException e) {
229 }
230 if (ai == null) {
231 return ActivityManager.COMPAT_MODE_UNKNOWN;
232 }
233 return computeCompatModeLocked(ai);
234 }
235
236 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
237 ApplicationInfo ai = null;
238 try {
239 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
240 } catch (RemoteException e) {
241 }
242 if (ai == null) {
243 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
244 return;
245 }
246 setPackageScreenCompatModeLocked(ai, mode);
247 }
248
249 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
250 final String packageName = ai.packageName;
251
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700252 int curFlags = getPackageFlags(packageName);
253
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700254 boolean enable;
255 switch (mode) {
256 case ActivityManager.COMPAT_MODE_DISABLED:
257 enable = false;
258 break;
259 case ActivityManager.COMPAT_MODE_ENABLED:
260 enable = true;
261 break;
262 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700263 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700264 break;
265 default:
266 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
267 return;
268 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700269
270 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700271 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700272 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700273 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700274 newFlags &= ~COMPAT_FLAG_ENABLED;
275 }
276
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700277 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
278 if (ci.alwaysSupportsScreen()) {
279 Slog.w(TAG, "Ignoring compat mode change of " + packageName
280 + "; compatibility never needed");
281 newFlags = 0;
282 }
283 if (ci.neverSupportsScreen()) {
284 Slog.w(TAG, "Ignoring compat mode change of " + packageName
285 + "; compatibility always needed");
286 newFlags = 0;
287 }
288
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700289 if (newFlags != curFlags) {
290 if (newFlags != 0) {
291 mPackages.put(packageName, newFlags);
292 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700293 mPackages.remove(packageName);
294 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700295
296 // Need to get compatibility info in new state.
297 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700298
299 mHandler.removeMessages(MSG_WRITE);
300 Message msg = mHandler.obtainMessage(MSG_WRITE);
301 mHandler.sendMessageDelayed(msg, 10000);
302
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700303 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
304
305 // All activities that came from the package must be
306 // restarted as if there was a config change.
307 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
308 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
309 if (a.info.packageName.equals(packageName)) {
310 a.forceNewConfig = true;
311 if (starting != null && a == starting && a.visible) {
312 a.startFreezingScreenLocked(starting.app,
313 ActivityInfo.CONFIG_SCREEN_LAYOUT);
314 }
315 }
316 }
317
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700318 // Tell all processes that loaded this package about the change.
319 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
320 ProcessRecord app = mService.mLruProcesses.get(i);
321 if (!app.pkgList.contains(packageName)) {
322 continue;
323 }
324 try {
325 if (app.thread != null) {
326 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
327 + app.processName + " new compat " + ci);
328 app.thread.updatePackageCompatibilityInfo(packageName, ci);
329 }
330 } catch (Exception e) {
331 }
332 }
333
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700334 if (starting != null) {
335 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
336 // And we need to make sure at this point that all other activities
337 // are made visible with the correct configuration.
338 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
339 }
340 }
341 }
342
343 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700344 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700345 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700346 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700347 }
348
349 FileOutputStream fos = null;
350
351 try {
352 fos = mFile.startWrite();
353 XmlSerializer out = new FastXmlSerializer();
354 out.setOutput(fos, "utf-8");
355 out.startDocument(null, true);
356 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
357 out.startTag(null, "compat-packages");
358
359 final IPackageManager pm = AppGlobals.getPackageManager();
360 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700361 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700362 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700363 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700364 Map.Entry<String, Integer> entry = it.next();
365 String pkg = entry.getKey();
366 int mode = entry.getValue();
367 if (mode == 0) {
368 continue;
369 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700370 ApplicationInfo ai = null;
371 try {
372 ai = pm.getApplicationInfo(pkg, 0);
373 } catch (RemoteException e) {
374 }
375 if (ai == null) {
376 continue;
377 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700378 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
379 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700380 if (info.alwaysSupportsScreen()) {
381 continue;
382 }
383 if (info.neverSupportsScreen()) {
384 continue;
385 }
386 out.startTag(null, "pkg");
387 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700388 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700389 out.endTag(null, "pkg");
390 }
391
392 out.endTag(null, "compat-packages");
393 out.endDocument();
394
395 mFile.finishWrite(fos);
396 } catch (java.io.IOException e1) {
397 Slog.w(TAG, "Error writing compat packages", e1);
398 if (fos != null) {
399 mFile.failWrite(fos);
400 }
401 }
402 }
403}