blob: 221b59b8307a39657401fac9e97c41a7151ed37d [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;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.IPackageManager;
22import android.content.res.CompatibilityInfo;
23import android.os.Handler;
24import android.os.Message;
25import android.os.RemoteException;
26import android.util.Slog;
27import android.util.Xml;
28
29public 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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070036 // 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>();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070042
43 private static final int MSG_WRITE = 1;
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
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) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070081 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);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070090 }
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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700111 public HashMap<String, Integer> getPackages() {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700112 return mPackages;
113 }
114
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700115 private int getPackageFlags(String packageName) {
116 Integer flags = mPackages.get(packageName);
117 return flags != null ? flags : 0;
118 }
119
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700120 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
121 return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700122 mService.mConfiguration.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700123 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700124 }
125
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700126 public int computeCompatModeLocked(ApplicationInfo ai) {
127 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700128 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700129 mService.mConfiguration.screenLayout,
130 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700131 if (info.alwaysSupportsScreen()) {
132 return ActivityManager.COMPAT_MODE_NEVER;
133 }
134 if (info.neverSupportsScreen()) {
135 return ActivityManager.COMPAT_MODE_ALWAYS;
136 }
137 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
138 : ActivityManager.COMPAT_MODE_DISABLED;
139 }
140
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700141 public boolean getFrontActivityAskCompatModeLocked() {
142 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
143 if (r == null) {
144 return false;
145 }
146 return getPackageAskCompatModeLocked(r.packageName);
147 }
148
149 public boolean getPackageAskCompatModeLocked(String packageName) {
150 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
151 }
152
153 public void setFrontActivityAskCompatModeLocked(boolean ask) {
154 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
155 if (r != null) {
156 setPackageAskCompatModeLocked(r.packageName, ask);
157 }
158 }
159
160 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
161 int curFlags = getPackageFlags(packageName);
162 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
163 if (curFlags != newFlags) {
164 if (newFlags != 0) {
165 mPackages.put(packageName, newFlags);
166 } else {
167 mPackages.remove(packageName);
168 }
169 mHandler.removeMessages(MSG_WRITE);
170 Message msg = mHandler.obtainMessage(MSG_WRITE);
171 mHandler.sendMessageDelayed(msg, 10000);
172 }
173 }
174
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700175 public int getFrontActivityScreenCompatModeLocked() {
176 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
177 if (r == null) {
178 return ActivityManager.COMPAT_MODE_UNKNOWN;
179 }
180 return computeCompatModeLocked(r.info.applicationInfo);
181 }
182
183 public void setFrontActivityScreenCompatModeLocked(int mode) {
184 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
185 if (r == null) {
186 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
187 return;
188 }
189 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
190 }
191
192 public int getPackageScreenCompatModeLocked(String packageName) {
193 ApplicationInfo ai = null;
194 try {
195 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
196 } catch (RemoteException e) {
197 }
198 if (ai == null) {
199 return ActivityManager.COMPAT_MODE_UNKNOWN;
200 }
201 return computeCompatModeLocked(ai);
202 }
203
204 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
205 ApplicationInfo ai = null;
206 try {
207 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
208 } catch (RemoteException e) {
209 }
210 if (ai == null) {
211 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
212 return;
213 }
214 setPackageScreenCompatModeLocked(ai, mode);
215 }
216
217 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
218 final String packageName = ai.packageName;
219
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700220 int curFlags = getPackageFlags(packageName);
221
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700222 boolean enable;
223 switch (mode) {
224 case ActivityManager.COMPAT_MODE_DISABLED:
225 enable = false;
226 break;
227 case ActivityManager.COMPAT_MODE_ENABLED:
228 enable = true;
229 break;
230 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700231 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700232 break;
233 default:
234 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
235 return;
236 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700237
238 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700239 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700240 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700241 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700242 newFlags &= ~COMPAT_FLAG_ENABLED;
243 }
244
245 if (newFlags != curFlags) {
246 if (newFlags != 0) {
247 mPackages.put(packageName, newFlags);
248 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700249 mPackages.remove(packageName);
250 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700251 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
252 if (ci.alwaysSupportsScreen()) {
253 Slog.w(TAG, "Ignoring compat mode change of " + packageName
254 + "; compatibility never needed");
255 return;
256 }
257 if (ci.neverSupportsScreen()) {
258 Slog.w(TAG, "Ignoring compat mode change of " + packageName
259 + "; compatibility always needed");
260 return;
261 }
262
263 mHandler.removeMessages(MSG_WRITE);
264 Message msg = mHandler.obtainMessage(MSG_WRITE);
265 mHandler.sendMessageDelayed(msg, 10000);
266
267 // Tell all processes that loaded this package about the change.
268 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
269 ProcessRecord app = mService.mLruProcesses.get(i);
270 if (!app.pkgList.contains(packageName)) {
271 continue;
272 }
273 try {
274 if (app.thread != null) {
275 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
276 + app.processName + " new compat " + ci);
277 app.thread.updatePackageCompatibilityInfo(packageName, ci);
278 }
279 } catch (Exception e) {
280 }
281 }
282
283 // All activities that came from the packge must be
284 // restarted as if there was a config change.
285 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
286 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
287 if (a.info.packageName.equals(packageName)) {
288 a.forceNewConfig = true;
289 }
290 }
291
292 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
293 if (starting != null) {
294 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
295 // And we need to make sure at this point that all other activities
296 // are made visible with the correct configuration.
297 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
298 }
299 }
300 }
301
302 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700303 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700304 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700305 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700306 }
307
308 FileOutputStream fos = null;
309
310 try {
311 fos = mFile.startWrite();
312 XmlSerializer out = new FastXmlSerializer();
313 out.setOutput(fos, "utf-8");
314 out.startDocument(null, true);
315 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
316 out.startTag(null, "compat-packages");
317
318 final IPackageManager pm = AppGlobals.getPackageManager();
319 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700320 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700321 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700322 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700323 Map.Entry<String, Integer> entry = it.next();
324 String pkg = entry.getKey();
325 int mode = entry.getValue();
326 if (mode == 0) {
327 continue;
328 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700329 ApplicationInfo ai = null;
330 try {
331 ai = pm.getApplicationInfo(pkg, 0);
332 } catch (RemoteException e) {
333 }
334 if (ai == null) {
335 continue;
336 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700337 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
338 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700339 if (info.alwaysSupportsScreen()) {
340 continue;
341 }
342 if (info.neverSupportsScreen()) {
343 continue;
344 }
345 out.startTag(null, "pkg");
346 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700347 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700348 out.endTag(null, "pkg");
349 }
350
351 out.endTag(null, "compat-packages");
352 out.endDocument();
353
354 mFile.finishWrite(fos);
355 } catch (java.io.IOException e1) {
356 Slog.w(TAG, "Error writing compat packages", e1);
357 if (fos != null) {
358 mFile.failWrite(fos);
359 }
360 }
361 }
362}