blob: 3ba3fbbb631da1613d07222ffc65ce36deb48ee3 [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
Dianne Hackborn40c8db52012-02-10 18:59:48 -080044 private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070045
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 {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700124 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700125 } 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
Dianne Hackborna9551702011-06-14 21:05:03 -0700134 if (updated) {
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700135 // 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
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700146 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700147 CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700148 mService.mConfiguration.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700149 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700150 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
151 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700152 }
153
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700154 public int computeCompatModeLocked(ApplicationInfo ai) {
155 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700156 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700157 mService.mConfiguration.screenLayout,
158 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700159 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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700169 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
177 public boolean getPackageAskCompatModeLocked(String packageName) {
178 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
179 }
180
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
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
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700203 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
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
220 public int getPackageScreenCompatModeLocked(String packageName) {
221 ApplicationInfo ai = null;
222 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700223 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700224 } catch (RemoteException e) {
225 }
226 if (ai == null) {
227 return ActivityManager.COMPAT_MODE_UNKNOWN;
228 }
229 return computeCompatModeLocked(ai);
230 }
231
232 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
233 ApplicationInfo ai = null;
234 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700235 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700236 } 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
245 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
246 final String packageName = ai.packageName;
247
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700248 int curFlags = getPackageFlags(packageName);
249
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700250 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:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700259 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700260 break;
261 default:
262 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
263 return;
264 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700265
266 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700267 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700268 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700269 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700270 newFlags &= ~COMPAT_FLAG_ENABLED;
271 }
272
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700273 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
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700285 if (newFlags != curFlags) {
286 if (newFlags != 0) {
287 mPackages.put(packageName, newFlags);
288 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700289 mPackages.remove(packageName);
290 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700291
292 // Need to get compatibility info in new state.
293 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700294
295 mHandler.removeMessages(MSG_WRITE);
296 Message msg = mHandler.obtainMessage(MSG_WRITE);
297 mHandler.sendMessageDelayed(msg, 10000);
298
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700299 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
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700314 // 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
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700330 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
339 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700340 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700341 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700342 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700343 }
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;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700357 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700358 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700359 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700360 Map.Entry<String, Integer> entry = it.next();
361 String pkg = entry.getKey();
362 int mode = entry.getValue();
363 if (mode == 0) {
364 continue;
365 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700366 ApplicationInfo ai = null;
367 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700368 ai = pm.getApplicationInfo(pkg, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700369 } catch (RemoteException e) {
370 }
371 if (ai == null) {
372 continue;
373 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700374 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
375 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700376 if (info.alwaysSupportsScreen()) {
377 continue;
378 }
379 if (info.neverSupportsScreen()) {
380 continue;
381 }
382 out.startTag(null, "pkg");
383 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700384 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700385 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}