blob: 1334bcd7147aec3105aace18b83b4258f8482b1c [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) {
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700153 CompatibilityInfo ci = 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 Hackborn2f0b1752011-05-31 17:59:49 -0700156 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
157 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700158 }
159
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700160 public int computeCompatModeLocked(ApplicationInfo ai) {
161 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700162 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700163 mService.mConfiguration.screenLayout,
164 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700165 if (info.alwaysSupportsScreen()) {
166 return ActivityManager.COMPAT_MODE_NEVER;
167 }
168 if (info.neverSupportsScreen()) {
169 return ActivityManager.COMPAT_MODE_ALWAYS;
170 }
171 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
172 : ActivityManager.COMPAT_MODE_DISABLED;
173 }
174
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700175 public boolean getFrontActivityAskCompatModeLocked() {
176 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
177 if (r == null) {
178 return false;
179 }
180 return getPackageAskCompatModeLocked(r.packageName);
181 }
182
183 public boolean getPackageAskCompatModeLocked(String packageName) {
184 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
185 }
186
187 public void setFrontActivityAskCompatModeLocked(boolean ask) {
188 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
189 if (r != null) {
190 setPackageAskCompatModeLocked(r.packageName, ask);
191 }
192 }
193
194 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
195 int curFlags = getPackageFlags(packageName);
196 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
197 if (curFlags != newFlags) {
198 if (newFlags != 0) {
199 mPackages.put(packageName, newFlags);
200 } else {
201 mPackages.remove(packageName);
202 }
203 mHandler.removeMessages(MSG_WRITE);
204 Message msg = mHandler.obtainMessage(MSG_WRITE);
205 mHandler.sendMessageDelayed(msg, 10000);
206 }
207 }
208
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700209 public int getFrontActivityScreenCompatModeLocked() {
210 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
211 if (r == null) {
212 return ActivityManager.COMPAT_MODE_UNKNOWN;
213 }
214 return computeCompatModeLocked(r.info.applicationInfo);
215 }
216
217 public void setFrontActivityScreenCompatModeLocked(int mode) {
218 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
219 if (r == null) {
220 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
221 return;
222 }
223 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
224 }
225
226 public int getPackageScreenCompatModeLocked(String packageName) {
227 ApplicationInfo ai = null;
228 try {
229 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
230 } catch (RemoteException e) {
231 }
232 if (ai == null) {
233 return ActivityManager.COMPAT_MODE_UNKNOWN;
234 }
235 return computeCompatModeLocked(ai);
236 }
237
238 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
239 ApplicationInfo ai = null;
240 try {
241 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
242 } catch (RemoteException e) {
243 }
244 if (ai == null) {
245 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
246 return;
247 }
248 setPackageScreenCompatModeLocked(ai, mode);
249 }
250
251 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
252 final String packageName = ai.packageName;
253
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700254 int curFlags = getPackageFlags(packageName);
255
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700256 boolean enable;
257 switch (mode) {
258 case ActivityManager.COMPAT_MODE_DISABLED:
259 enable = false;
260 break;
261 case ActivityManager.COMPAT_MODE_ENABLED:
262 enable = true;
263 break;
264 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700265 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700266 break;
267 default:
268 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
269 return;
270 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700271
272 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700273 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700274 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700275 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700276 newFlags &= ~COMPAT_FLAG_ENABLED;
277 }
278
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700279 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
280 if (ci.alwaysSupportsScreen()) {
281 Slog.w(TAG, "Ignoring compat mode change of " + packageName
282 + "; compatibility never needed");
283 newFlags = 0;
284 }
285 if (ci.neverSupportsScreen()) {
286 Slog.w(TAG, "Ignoring compat mode change of " + packageName
287 + "; compatibility always needed");
288 newFlags = 0;
289 }
290
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700291 if (newFlags != curFlags) {
292 if (newFlags != 0) {
293 mPackages.put(packageName, newFlags);
294 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700295 mPackages.remove(packageName);
296 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700297
298 // Need to get compatibility info in new state.
299 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700300
301 mHandler.removeMessages(MSG_WRITE);
302 Message msg = mHandler.obtainMessage(MSG_WRITE);
303 mHandler.sendMessageDelayed(msg, 10000);
304
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700305 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
306
307 // All activities that came from the package must be
308 // restarted as if there was a config change.
309 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
310 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
311 if (a.info.packageName.equals(packageName)) {
312 a.forceNewConfig = true;
313 if (starting != null && a == starting && a.visible) {
314 a.startFreezingScreenLocked(starting.app,
315 ActivityInfo.CONFIG_SCREEN_LAYOUT);
316 }
317 }
318 }
319
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700320 // Tell all processes that loaded this package about the change.
321 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
322 ProcessRecord app = mService.mLruProcesses.get(i);
323 if (!app.pkgList.contains(packageName)) {
324 continue;
325 }
326 try {
327 if (app.thread != null) {
328 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
329 + app.processName + " new compat " + ci);
330 app.thread.updatePackageCompatibilityInfo(packageName, ci);
331 }
332 } catch (Exception e) {
333 }
334 }
335
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700336 if (starting != null) {
337 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
338 // And we need to make sure at this point that all other activities
339 // are made visible with the correct configuration.
340 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
341 }
342 }
343 }
344
345 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700346 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700347 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700348 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700349 }
350
351 FileOutputStream fos = null;
352
353 try {
354 fos = mFile.startWrite();
355 XmlSerializer out = new FastXmlSerializer();
356 out.setOutput(fos, "utf-8");
357 out.startDocument(null, true);
358 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
359 out.startTag(null, "compat-packages");
360
361 final IPackageManager pm = AppGlobals.getPackageManager();
362 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700363 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700364 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700365 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700366 Map.Entry<String, Integer> entry = it.next();
367 String pkg = entry.getKey();
368 int mode = entry.getValue();
369 if (mode == 0) {
370 continue;
371 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700372 ApplicationInfo ai = null;
373 try {
374 ai = pm.getApplicationInfo(pkg, 0);
375 } catch (RemoteException e) {
376 }
377 if (ai == null) {
378 continue;
379 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700380 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
381 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700382 if (info.alwaysSupportsScreen()) {
383 continue;
384 }
385 if (info.neverSupportsScreen()) {
386 continue;
387 }
388 out.startTag(null, "pkg");
389 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700390 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700391 out.endTag(null, "pkg");
392 }
393
394 out.endTag(null, "compat-packages");
395 out.endDocument();
396
397 mFile.finishWrite(fos);
398 } catch (java.io.IOException e1) {
399 Slog.w(TAG, "Error writing compat packages", e1);
400 if (fos != null) {
401 mFile.failWrite(fos);
402 }
403 }
404 }
405}