blob: 3a6492e2957b1a527954e53792d13e334bf02594 [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;
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -070018import android.content.pm.ActivityInfo;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070019import android.content.pm.ApplicationInfo;
20import android.content.pm.IPackageManager;
21import android.content.res.CompatibilityInfo;
22import android.os.Handler;
23import android.os.Message;
24import android.os.RemoteException;
Dianne Hackborn39606a02012-07-31 17:54:35 -070025import android.util.AtomicFile;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070026import 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
Dianne Hackborn40c8db52012-02-10 18:59:48 -080043 private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070044
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 Hackborn8ea5e1d2011-05-27 16:45:31 -0700120 public void handlePackageAddedLocked(String packageName, boolean updated) {
121 ApplicationInfo ai = null;
122 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700123 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700124 } catch (RemoteException e) {
125 }
126 if (ai == null) {
127 return;
128 }
129 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
130 final boolean mayCompat = !ci.alwaysSupportsScreen()
131 && !ci.neverSupportsScreen();
132
Dianne Hackborna9551702011-06-14 21:05:03 -0700133 if (updated) {
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700134 // Update -- if the app no longer can run in compat mode, clear
135 // any current settings for it.
136 if (!mayCompat && mPackages.containsKey(packageName)) {
137 mPackages.remove(packageName);
138 mHandler.removeMessages(MSG_WRITE);
139 Message msg = mHandler.obtainMessage(MSG_WRITE);
140 mHandler.sendMessageDelayed(msg, 10000);
141 }
142 }
143 }
144
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700145 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700146 CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700147 mService.mConfiguration.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700148 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700149 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
150 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700151 }
152
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700153 public int computeCompatModeLocked(ApplicationInfo ai) {
154 boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700155 CompatibilityInfo info = new CompatibilityInfo(ai,
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700156 mService.mConfiguration.screenLayout,
157 mService.mConfiguration.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700158 if (info.alwaysSupportsScreen()) {
159 return ActivityManager.COMPAT_MODE_NEVER;
160 }
161 if (info.neverSupportsScreen()) {
162 return ActivityManager.COMPAT_MODE_ALWAYS;
163 }
164 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
165 : ActivityManager.COMPAT_MODE_DISABLED;
166 }
167
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700168 public boolean getFrontActivityAskCompatModeLocked() {
169 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
170 if (r == null) {
171 return false;
172 }
173 return getPackageAskCompatModeLocked(r.packageName);
174 }
175
176 public boolean getPackageAskCompatModeLocked(String packageName) {
177 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
178 }
179
180 public void setFrontActivityAskCompatModeLocked(boolean ask) {
181 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
182 if (r != null) {
183 setPackageAskCompatModeLocked(r.packageName, ask);
184 }
185 }
186
187 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
188 int curFlags = getPackageFlags(packageName);
189 int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
190 if (curFlags != newFlags) {
191 if (newFlags != 0) {
192 mPackages.put(packageName, newFlags);
193 } else {
194 mPackages.remove(packageName);
195 }
196 mHandler.removeMessages(MSG_WRITE);
197 Message msg = mHandler.obtainMessage(MSG_WRITE);
198 mHandler.sendMessageDelayed(msg, 10000);
199 }
200 }
201
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700202 public int getFrontActivityScreenCompatModeLocked() {
203 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
204 if (r == null) {
205 return ActivityManager.COMPAT_MODE_UNKNOWN;
206 }
207 return computeCompatModeLocked(r.info.applicationInfo);
208 }
209
210 public void setFrontActivityScreenCompatModeLocked(int mode) {
211 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
212 if (r == null) {
213 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
214 return;
215 }
216 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
217 }
218
219 public int getPackageScreenCompatModeLocked(String packageName) {
220 ApplicationInfo ai = null;
221 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700222 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700223 } catch (RemoteException e) {
224 }
225 if (ai == null) {
226 return ActivityManager.COMPAT_MODE_UNKNOWN;
227 }
228 return computeCompatModeLocked(ai);
229 }
230
231 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
232 ApplicationInfo ai = null;
233 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700234 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700235 } catch (RemoteException e) {
236 }
237 if (ai == null) {
238 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
239 return;
240 }
241 setPackageScreenCompatModeLocked(ai, mode);
242 }
243
244 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
245 final String packageName = ai.packageName;
246
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700247 int curFlags = getPackageFlags(packageName);
248
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700249 boolean enable;
250 switch (mode) {
251 case ActivityManager.COMPAT_MODE_DISABLED:
252 enable = false;
253 break;
254 case ActivityManager.COMPAT_MODE_ENABLED:
255 enable = true;
256 break;
257 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700258 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700259 break;
260 default:
261 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
262 return;
263 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700264
265 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700266 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700267 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700268 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700269 newFlags &= ~COMPAT_FLAG_ENABLED;
270 }
271
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700272 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
273 if (ci.alwaysSupportsScreen()) {
274 Slog.w(TAG, "Ignoring compat mode change of " + packageName
275 + "; compatibility never needed");
276 newFlags = 0;
277 }
278 if (ci.neverSupportsScreen()) {
279 Slog.w(TAG, "Ignoring compat mode change of " + packageName
280 + "; compatibility always needed");
281 newFlags = 0;
282 }
283
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700284 if (newFlags != curFlags) {
285 if (newFlags != 0) {
286 mPackages.put(packageName, newFlags);
287 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700288 mPackages.remove(packageName);
289 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700290
291 // Need to get compatibility info in new state.
292 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700293
294 mHandler.removeMessages(MSG_WRITE);
295 Message msg = mHandler.obtainMessage(MSG_WRITE);
296 mHandler.sendMessageDelayed(msg, 10000);
297
Craig Mautner2ad92072013-02-25 16:19:24 -0800298 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
299
300 // All activities that came from the package must be
301 // restarted as if there was a config change.
302 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
303 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
304 if (a.info.packageName.equals(packageName)) {
305 a.forceNewConfig = true;
306 if (starting != null && a == starting && a.visible) {
307 a.startFreezingScreenLocked(starting.app,
308 ActivityInfo.CONFIG_SCREEN_LAYOUT);
309 }
310 }
311 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700312
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700313 // Tell all processes that loaded this package about the change.
314 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
315 ProcessRecord app = mService.mLruProcesses.get(i);
316 if (!app.pkgList.contains(packageName)) {
317 continue;
318 }
319 try {
320 if (app.thread != null) {
321 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
322 + app.processName + " new compat " + ci);
323 app.thread.updatePackageCompatibilityInfo(packageName, ci);
324 }
325 } catch (Exception e) {
326 }
327 }
328
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700329 if (starting != null) {
330 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
331 // And we need to make sure at this point that all other activities
332 // are made visible with the correct configuration.
333 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
334 }
335 }
336 }
337
338 void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700339 HashMap<String, Integer> pkgs;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700340 synchronized (mService) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700341 pkgs = new HashMap<String, Integer>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700342 }
343
344 FileOutputStream fos = null;
345
346 try {
347 fos = mFile.startWrite();
348 XmlSerializer out = new FastXmlSerializer();
349 out.setOutput(fos, "utf-8");
350 out.startDocument(null, true);
351 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
352 out.startTag(null, "compat-packages");
353
354 final IPackageManager pm = AppGlobals.getPackageManager();
355 final int screenLayout = mService.mConfiguration.screenLayout;
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700356 final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700357 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700358 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700359 Map.Entry<String, Integer> entry = it.next();
360 String pkg = entry.getKey();
361 int mode = entry.getValue();
362 if (mode == 0) {
363 continue;
364 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700365 ApplicationInfo ai = null;
366 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700367 ai = pm.getApplicationInfo(pkg, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700368 } catch (RemoteException e) {
369 }
370 if (ai == null) {
371 continue;
372 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700373 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
374 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700375 if (info.alwaysSupportsScreen()) {
376 continue;
377 }
378 if (info.neverSupportsScreen()) {
379 continue;
380 }
381 out.startTag(null, "pkg");
382 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700383 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700384 out.endTag(null, "pkg");
385 }
386
387 out.endTag(null, "compat-packages");
388 out.endDocument();
389
390 mFile.finishWrite(fos);
391 } catch (java.io.IOException e1) {
392 Slog.w(TAG, "Error writing compat packages", e1);
393 if (fos != null) {
394 mFile.failWrite(fos);
395 }
396 }
397 }
398}