blob: 59e678738444f76988691bb0e03396d4dff2e6c3 [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.Iterator;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -07008import java.util.Map;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -07009
10import org.xmlpull.v1.XmlPullParser;
11import org.xmlpull.v1.XmlPullParserException;
12import org.xmlpull.v1.XmlSerializer;
13
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070014import com.android.internal.util.FastXmlSerializer;
15
16import android.app.ActivityManager;
17import android.app.AppGlobals;
18import android.content.pm.ApplicationInfo;
19import android.content.pm.IPackageManager;
20import android.content.res.CompatibilityInfo;
21import android.os.Handler;
22import android.os.Message;
23import android.os.RemoteException;
Dianne Hackborn39606a02012-07-31 17:54:35 -070024import android.util.AtomicFile;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070025import android.util.Slog;
26import android.util.Xml;
27
Dianne Hackbornbe4e6aa2013-06-07 13:25:29 -070028public final class CompatModePackages {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070029 private final String TAG = ActivityManagerService.TAG;
30 private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
31
32 private final ActivityManagerService mService;
33 private final AtomicFile mFile;
34
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070035 // Compatibility state: no longer ask user to select the mode.
36 public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
37 // Compatibility state: compatibility mode is enabled.
38 public static final int COMPAT_FLAG_ENABLED = 1<<1;
39
40 private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070041
Dianne Hackborn40c8db52012-02-10 18:59:48 -080042 private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070043
44 private final Handler mHandler = new Handler() {
45 @Override public void handleMessage(Message msg) {
46 switch (msg.what) {
47 case MSG_WRITE:
48 saveCompatModes();
49 break;
50 default:
51 super.handleMessage(msg);
52 break;
53 }
54 }
55 };
56
57 public CompatModePackages(ActivityManagerService service, File systemDir) {
58 mService = service;
59 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"));
60
61 FileInputStream fis = null;
62 try {
63 fis = mFile.openRead();
64 XmlPullParser parser = Xml.newPullParser();
65 parser.setInput(fis, null);
66 int eventType = parser.getEventType();
67 while (eventType != XmlPullParser.START_TAG) {
68 eventType = parser.next();
69 }
70 String tagName = parser.getName();
71 if ("compat-packages".equals(tagName)) {
72 eventType = parser.next();
73 do {
74 if (eventType == XmlPullParser.START_TAG) {
75 tagName = parser.getName();
76 if (parser.getDepth() == 2) {
77 if ("pkg".equals(tagName)) {
78 String pkg = parser.getAttributeValue(null, "name");
79 if (pkg != null) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070080 String mode = parser.getAttributeValue(null, "mode");
81 int modeInt = 0;
82 if (mode != null) {
83 try {
84 modeInt = Integer.parseInt(mode);
85 } catch (NumberFormatException e) {
86 }
87 }
88 mPackages.put(pkg, modeInt);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070089 }
90 }
91 }
92 }
93 eventType = parser.next();
94 } while (eventType != XmlPullParser.END_DOCUMENT);
95 }
96 } catch (XmlPullParserException e) {
97 Slog.w(TAG, "Error reading compat-packages", e);
98 } catch (java.io.IOException e) {
99 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
100 } finally {
101 if (fis != null) {
102 try {
103 fis.close();
104 } catch (java.io.IOException e1) {
105 }
106 }
107 }
108 }
109
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700110 public HashMap<String, Integer> getPackages() {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700111 return mPackages;
112 }
113
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700114 private int getPackageFlags(String packageName) {
115 Integer flags = mPackages.get(packageName);
116 return flags != null ? flags : 0;
117 }
118
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700119 public void handlePackageAddedLocked(String packageName, boolean updated) {
120 ApplicationInfo ai = null;
121 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700122 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700123 } catch (RemoteException e) {
124 }
125 if (ai == null) {
126 return;
127 }
128 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
129 final boolean mayCompat = !ci.alwaysSupportsScreen()
130 && !ci.neverSupportsScreen();
131
Dianne Hackborna9551702011-06-14 21:05:03 -0700132 if (updated) {
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700133 // Update -- if the app no longer can run in compat mode, clear
134 // any current settings for it.
135 if (!mayCompat && mPackages.containsKey(packageName)) {
136 mPackages.remove(packageName);
137 mHandler.removeMessages(MSG_WRITE);
138 Message msg = mHandler.obtainMessage(MSG_WRITE);
139 mHandler.sendMessageDelayed(msg, 10000);
140 }
141 }
142 }
143
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700144 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700145 CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700146 mService.mConfiguration.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700147 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700148 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
149 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700150 }
151
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700152 public int computeCompatModeLocked(ApplicationInfo ai) {
153 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700154 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700155 mService.mConfiguration.screenLayout,
156 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700157 if (info.alwaysSupportsScreen()) {
158 return ActivityManager.COMPAT_MODE_NEVER;
159 }
160 if (info.neverSupportsScreen()) {
161 return ActivityManager.COMPAT_MODE_ALWAYS;
162 }
163 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
164 : ActivityManager.COMPAT_MODE_DISABLED;
165 }
166
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700167 public boolean getFrontActivityAskCompatModeLocked() {
Craig Mautnerce5f3cb2013-04-22 08:58:54 -0700168 ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700169 if (r == null) {
170 return false;
171 }
172 return getPackageAskCompatModeLocked(r.packageName);
173 }
174
175 public boolean getPackageAskCompatModeLocked(String packageName) {
176 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
177 }
178
179 public void setFrontActivityAskCompatModeLocked(boolean ask) {
Craig Mautnerce5f3cb2013-04-22 08:58:54 -0700180 ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700181 if (r != null) {
182 setPackageAskCompatModeLocked(r.packageName, ask);
183 }
184 }
185
186 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
187 int curFlags = getPackageFlags(packageName);
188 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
189 if (curFlags != newFlags) {
190 if (newFlags != 0) {
191 mPackages.put(packageName, newFlags);
192 } else {
193 mPackages.remove(packageName);
194 }
195 mHandler.removeMessages(MSG_WRITE);
196 Message msg = mHandler.obtainMessage(MSG_WRITE);
197 mHandler.sendMessageDelayed(msg, 10000);
198 }
199 }
200
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700201 public int getFrontActivityScreenCompatModeLocked() {
Craig Mautnerce5f3cb2013-04-22 08:58:54 -0700202 ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700203 if (r == null) {
204 return ActivityManager.COMPAT_MODE_UNKNOWN;
205 }
206 return computeCompatModeLocked(r.info.applicationInfo);
207 }
208
209 public void setFrontActivityScreenCompatModeLocked(int mode) {
Craig Mautnerce5f3cb2013-04-22 08:58:54 -0700210 ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700211 if (r == null) {
212 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
213 return;
214 }
215 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
216 }
217
218 public int getPackageScreenCompatModeLocked(String packageName) {
219 ApplicationInfo ai = null;
220 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700221 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700222 } catch (RemoteException e) {
223 }
224 if (ai == null) {
225 return ActivityManager.COMPAT_MODE_UNKNOWN;
226 }
227 return computeCompatModeLocked(ai);
228 }
229
230 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
231 ApplicationInfo ai = null;
232 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700233 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700234 } catch (RemoteException e) {
235 }
236 if (ai == null) {
237 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
238 return;
239 }
240 setPackageScreenCompatModeLocked(ai, mode);
241 }
242
243 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
244 final String packageName = ai.packageName;
245
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700246 int curFlags = getPackageFlags(packageName);
247
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700248 boolean enable;
249 switch (mode) {
250 case ActivityManager.COMPAT_MODE_DISABLED:
251 enable = false;
252 break;
253 case ActivityManager.COMPAT_MODE_ENABLED:
254 enable = true;
255 break;
256 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700257 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700258 break;
259 default:
260 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
261 return;
262 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700263
264 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700265 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700266 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700267 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700268 newFlags &= ~COMPAT_FLAG_ENABLED;
269 }
270
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700271 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
272 if (ci.alwaysSupportsScreen()) {
273 Slog.w(TAG, "Ignoring compat mode change of " + packageName
274 + "; compatibility never needed");
275 newFlags = 0;
276 }
277 if (ci.neverSupportsScreen()) {
278 Slog.w(TAG, "Ignoring compat mode change of " + packageName
279 + "; compatibility always needed");
280 newFlags = 0;
281 }
282
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700283 if (newFlags != curFlags) {
284 if (newFlags != 0) {
285 mPackages.put(packageName, newFlags);
286 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700287 mPackages.remove(packageName);
288 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700289
290 // Need to get compatibility info in new state.
291 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700292
293 mHandler.removeMessages(MSG_WRITE);
294 Message msg = mHandler.obtainMessage(MSG_WRITE);
295 mHandler.sendMessageDelayed(msg, 10000);
296
Craig Mautnerce5f3cb2013-04-22 08:58:54 -0700297 final ActivityStack stack = mService.getFocusedStack();
Craig Mautner20e72272013-04-01 13:45:53 -0700298 ActivityRecord starting = stack.restartPackage(packageName);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700299
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700300 // Tell all processes that loaded this package about the change.
301 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
302 ProcessRecord app = mService.mLruProcesses.get(i);
Dianne Hackborn78a369c2013-06-11 17:10:32 -0700303 if (!app.pkgList.containsKey(packageName)) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700304 continue;
305 }
306 try {
307 if (app.thread != null) {
308 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
309 + app.processName + " new compat " + ci);
310 app.thread.updatePackageCompatibilityInfo(packageName, ci);
311 }
312 } catch (Exception e) {
313 }
314 }
315
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700316 if (starting != null) {
Craig Mautner20e72272013-04-01 13:45:53 -0700317 stack.ensureActivityConfigurationLocked(starting, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700318 // And we need to make sure at this point that all other activities
319 // are made visible with the correct configuration.
Craig Mautner20e72272013-04-01 13:45:53 -0700320 stack.ensureActivitiesVisibleLocked(starting, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700321 }
322 }
323 }
324
325 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700326 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700327 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700328 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700329 }
330
331 FileOutputStream fos = null;
332
333 try {
334 fos = mFile.startWrite();
335 XmlSerializer out = new FastXmlSerializer();
336 out.setOutput(fos, "utf-8");
337 out.startDocument(null, true);
338 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
339 out.startTag(null, "compat-packages");
340
341 final IPackageManager pm = AppGlobals.getPackageManager();
342 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700343 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700344 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700345 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700346 Map.Entry<String, Integer> entry = it.next();
347 String pkg = entry.getKey();
348 int mode = entry.getValue();
349 if (mode == 0) {
350 continue;
351 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700352 ApplicationInfo ai = null;
353 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700354 ai = pm.getApplicationInfo(pkg, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700355 } catch (RemoteException e) {
356 }
357 if (ai == null) {
358 continue;
359 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700360 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
361 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700362 if (info.alwaysSupportsScreen()) {
363 continue;
364 }
365 if (info.neverSupportsScreen()) {
366 continue;
367 }
368 out.startTag(null, "pkg");
369 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700370 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700371 out.endTag(null, "pkg");
372 }
373
374 out.endTag(null, "compat-packages");
375 out.endDocument();
376
377 mFile.finishWrite(fos);
378 } catch (java.io.IOException e1) {
379 Slog.w(TAG, "Error writing compat packages", e1);
380 if (fos != null) {
381 mFile.failWrite(fos);
382 }
383 }
384 }
385}