blob: 1277bca5ed5180eb73114456346cbdce8e86a57a [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 Hackborn36cd41f2011-05-25 21:00:46 -0700122 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700123 }
124
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700125 public int computeCompatModeLocked(ApplicationInfo ai) {
126 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700127 CompatibilityInfo info = new CompatibilityInfo(ai,
128 mService.mConfiguration.screenLayout, enabled);
129 if (info.alwaysSupportsScreen()) {
130 return ActivityManager.COMPAT_MODE_NEVER;
131 }
132 if (info.neverSupportsScreen()) {
133 return ActivityManager.COMPAT_MODE_ALWAYS;
134 }
135 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
136 : ActivityManager.COMPAT_MODE_DISABLED;
137 }
138
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700139 public boolean getFrontActivityAskCompatModeLocked() {
140 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
141 if (r == null) {
142 return false;
143 }
144 return getPackageAskCompatModeLocked(r.packageName);
145 }
146
147 public boolean getPackageAskCompatModeLocked(String packageName) {
148 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
149 }
150
151 public void setFrontActivityAskCompatModeLocked(boolean ask) {
152 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
153 if (r != null) {
154 setPackageAskCompatModeLocked(r.packageName, ask);
155 }
156 }
157
158 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
159 int curFlags = getPackageFlags(packageName);
160 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
161 if (curFlags != newFlags) {
162 if (newFlags != 0) {
163 mPackages.put(packageName, newFlags);
164 } else {
165 mPackages.remove(packageName);
166 }
167 mHandler.removeMessages(MSG_WRITE);
168 Message msg = mHandler.obtainMessage(MSG_WRITE);
169 mHandler.sendMessageDelayed(msg, 10000);
170 }
171 }
172
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700173 public int getFrontActivityScreenCompatModeLocked() {
174 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
175 if (r == null) {
176 return ActivityManager.COMPAT_MODE_UNKNOWN;
177 }
178 return computeCompatModeLocked(r.info.applicationInfo);
179 }
180
181 public void setFrontActivityScreenCompatModeLocked(int mode) {
182 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
183 if (r == null) {
184 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
185 return;
186 }
187 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
188 }
189
190 public int getPackageScreenCompatModeLocked(String packageName) {
191 ApplicationInfo ai = null;
192 try {
193 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
194 } catch (RemoteException e) {
195 }
196 if (ai == null) {
197 return ActivityManager.COMPAT_MODE_UNKNOWN;
198 }
199 return computeCompatModeLocked(ai);
200 }
201
202 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
203 ApplicationInfo ai = null;
204 try {
205 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
206 } catch (RemoteException e) {
207 }
208 if (ai == null) {
209 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
210 return;
211 }
212 setPackageScreenCompatModeLocked(ai, mode);
213 }
214
215 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
216 final String packageName = ai.packageName;
217
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700218 int curFlags = getPackageFlags(packageName);
219
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700220 boolean enable;
221 switch (mode) {
222 case ActivityManager.COMPAT_MODE_DISABLED:
223 enable = false;
224 break;
225 case ActivityManager.COMPAT_MODE_ENABLED:
226 enable = true;
227 break;
228 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700229 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700230 break;
231 default:
232 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
233 return;
234 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700235
236 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700237 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700238 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700239 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700240 newFlags &= ~COMPAT_FLAG_ENABLED;
241 }
242
243 if (newFlags != curFlags) {
244 if (newFlags != 0) {
245 mPackages.put(packageName, newFlags);
246 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700247 mPackages.remove(packageName);
248 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700249 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
250 if (ci.alwaysSupportsScreen()) {
251 Slog.w(TAG, "Ignoring compat mode change of " + packageName
252 + "; compatibility never needed");
253 return;
254 }
255 if (ci.neverSupportsScreen()) {
256 Slog.w(TAG, "Ignoring compat mode change of " + packageName
257 + "; compatibility always needed");
258 return;
259 }
260
261 mHandler.removeMessages(MSG_WRITE);
262 Message msg = mHandler.obtainMessage(MSG_WRITE);
263 mHandler.sendMessageDelayed(msg, 10000);
264
265 // Tell all processes that loaded this package about the change.
266 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
267 ProcessRecord app = mService.mLruProcesses.get(i);
268 if (!app.pkgList.contains(packageName)) {
269 continue;
270 }
271 try {
272 if (app.thread != null) {
273 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
274 + app.processName + " new compat " + ci);
275 app.thread.updatePackageCompatibilityInfo(packageName, ci);
276 }
277 } catch (Exception e) {
278 }
279 }
280
281 // All activities that came from the packge must be
282 // restarted as if there was a config change.
283 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
284 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
285 if (a.info.packageName.equals(packageName)) {
286 a.forceNewConfig = true;
287 }
288 }
289
290 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
291 if (starting != null) {
292 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
293 // And we need to make sure at this point that all other activities
294 // are made visible with the correct configuration.
295 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
296 }
297 }
298 }
299
300 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700301 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700302 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700303 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700304 }
305
306 FileOutputStream fos = null;
307
308 try {
309 fos = mFile.startWrite();
310 XmlSerializer out = new FastXmlSerializer();
311 out.setOutput(fos, "utf-8");
312 out.startDocument(null, true);
313 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
314 out.startTag(null, "compat-packages");
315
316 final IPackageManager pm = AppGlobals.getPackageManager();
317 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700318 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700319 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700320 Map.Entry<String, Integer> entry = it.next();
321 String pkg = entry.getKey();
322 int mode = entry.getValue();
323 if (mode == 0) {
324 continue;
325 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700326 ApplicationInfo ai = null;
327 try {
328 ai = pm.getApplicationInfo(pkg, 0);
329 } catch (RemoteException e) {
330 }
331 if (ai == null) {
332 continue;
333 }
334 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, false);
335 if (info.alwaysSupportsScreen()) {
336 continue;
337 }
338 if (info.neverSupportsScreen()) {
339 continue;
340 }
341 out.startTag(null, "pkg");
342 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700343 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700344 out.endTag(null, "pkg");
345 }
346
347 out.endTag(null, "compat-packages");
348 out.endDocument();
349
350 mFile.finishWrite(fos);
351 } catch (java.io.IOException e1) {
352 Slog.w(TAG, "Error writing compat packages", e1);
353 if (fos != null) {
354 mFile.failWrite(fos);
355 }
356 }
357 }
358}