blob: 1faf8da0f47f86b994416da4b27b8c32ede0fc74 [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;
6import java.util.HashSet;
7import java.util.Iterator;
8
9import org.xmlpull.v1.XmlPullParser;
10import org.xmlpull.v1.XmlPullParserException;
11import org.xmlpull.v1.XmlSerializer;
12
13import com.android.internal.os.AtomicFile;
14import 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;
24import android.util.Slog;
25import android.util.Xml;
26
27public class CompatModePackages {
28 private final String TAG = ActivityManagerService.TAG;
29 private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
30
31 private final ActivityManagerService mService;
32 private final AtomicFile mFile;
33
34 private final HashSet<String> mPackages = new HashSet<String>();
35
36 private static final int MSG_WRITE = 1;
37
38 private final Handler mHandler = new Handler() {
39 @Override public void handleMessage(Message msg) {
40 switch (msg.what) {
41 case MSG_WRITE:
42 saveCompatModes();
43 break;
44 default:
45 super.handleMessage(msg);
46 break;
47 }
48 }
49 };
50
51 public CompatModePackages(ActivityManagerService service, File systemDir) {
52 mService = service;
53 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"));
54
55 FileInputStream fis = null;
56 try {
57 fis = mFile.openRead();
58 XmlPullParser parser = Xml.newPullParser();
59 parser.setInput(fis, null);
60 int eventType = parser.getEventType();
61 while (eventType != XmlPullParser.START_TAG) {
62 eventType = parser.next();
63 }
64 String tagName = parser.getName();
65 if ("compat-packages".equals(tagName)) {
66 eventType = parser.next();
67 do {
68 if (eventType == XmlPullParser.START_TAG) {
69 tagName = parser.getName();
70 if (parser.getDepth() == 2) {
71 if ("pkg".equals(tagName)) {
72 String pkg = parser.getAttributeValue(null, "name");
73 if (pkg != null) {
74 mPackages.add(pkg);
75 }
76 }
77 }
78 }
79 eventType = parser.next();
80 } while (eventType != XmlPullParser.END_DOCUMENT);
81 }
82 } catch (XmlPullParserException e) {
83 Slog.w(TAG, "Error reading compat-packages", e);
84 } catch (java.io.IOException e) {
85 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
86 } finally {
87 if (fis != null) {
88 try {
89 fis.close();
90 } catch (java.io.IOException e1) {
91 }
92 }
93 }
94 }
95
96 public HashSet<String> getPackages() {
97 return mPackages;
98 }
99
100 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
101 return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
102 mPackages.contains(ai.packageName));
103 }
104
105 private int computeCompatModeLocked(ApplicationInfo ai) {
106 boolean enabled = mPackages.contains(ai.packageName);
107 CompatibilityInfo info = new CompatibilityInfo(ai,
108 mService.mConfiguration.screenLayout, enabled);
109 if (info.alwaysSupportsScreen()) {
110 return ActivityManager.COMPAT_MODE_NEVER;
111 }
112 if (info.neverSupportsScreen()) {
113 return ActivityManager.COMPAT_MODE_ALWAYS;
114 }
115 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
116 : ActivityManager.COMPAT_MODE_DISABLED;
117 }
118
119 public int getFrontActivityScreenCompatModeLocked() {
120 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
121 if (r == null) {
122 return ActivityManager.COMPAT_MODE_UNKNOWN;
123 }
124 return computeCompatModeLocked(r.info.applicationInfo);
125 }
126
127 public void setFrontActivityScreenCompatModeLocked(int mode) {
128 ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
129 if (r == null) {
130 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
131 return;
132 }
133 setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
134 }
135
136 public int getPackageScreenCompatModeLocked(String packageName) {
137 ApplicationInfo ai = null;
138 try {
139 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
140 } catch (RemoteException e) {
141 }
142 if (ai == null) {
143 return ActivityManager.COMPAT_MODE_UNKNOWN;
144 }
145 return computeCompatModeLocked(ai);
146 }
147
148 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
149 ApplicationInfo ai = null;
150 try {
151 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0);
152 } catch (RemoteException e) {
153 }
154 if (ai == null) {
155 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
156 return;
157 }
158 setPackageScreenCompatModeLocked(ai, mode);
159 }
160
161 private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
162 final String packageName = ai.packageName;
163
164 boolean changed = false;
165 boolean enable;
166 switch (mode) {
167 case ActivityManager.COMPAT_MODE_DISABLED:
168 enable = false;
169 break;
170 case ActivityManager.COMPAT_MODE_ENABLED:
171 enable = true;
172 break;
173 case ActivityManager.COMPAT_MODE_TOGGLE:
174 enable = !mPackages.contains(packageName);
175 break;
176 default:
177 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
178 return;
179 }
180 if (enable) {
181 if (!mPackages.contains(packageName)) {
182 changed = true;
183 mPackages.add(packageName);
184 }
185 } else {
186 if (mPackages.contains(packageName)) {
187 changed = true;
188 mPackages.remove(packageName);
189 }
190 }
191 if (changed) {
192 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
193 if (ci.alwaysSupportsScreen()) {
194 Slog.w(TAG, "Ignoring compat mode change of " + packageName
195 + "; compatibility never needed");
196 return;
197 }
198 if (ci.neverSupportsScreen()) {
199 Slog.w(TAG, "Ignoring compat mode change of " + packageName
200 + "; compatibility always needed");
201 return;
202 }
203
204 mHandler.removeMessages(MSG_WRITE);
205 Message msg = mHandler.obtainMessage(MSG_WRITE);
206 mHandler.sendMessageDelayed(msg, 10000);
207
208 // Tell all processes that loaded this package about the change.
209 for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
210 ProcessRecord app = mService.mLruProcesses.get(i);
211 if (!app.pkgList.contains(packageName)) {
212 continue;
213 }
214 try {
215 if (app.thread != null) {
216 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
217 + app.processName + " new compat " + ci);
218 app.thread.updatePackageCompatibilityInfo(packageName, ci);
219 }
220 } catch (Exception e) {
221 }
222 }
223
224 // All activities that came from the packge must be
225 // restarted as if there was a config change.
226 for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) {
227 ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i);
228 if (a.info.packageName.equals(packageName)) {
229 a.forceNewConfig = true;
230 }
231 }
232
233 ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null);
234 if (starting != null) {
235 mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
236 // And we need to make sure at this point that all other activities
237 // are made visible with the correct configuration.
238 mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0);
239 }
240 }
241 }
242
243 void saveCompatModes() {
244 HashSet<String> pkgs;
245 synchronized (mService) {
246 pkgs = new HashSet<String>(mPackages);
247 }
248
249 FileOutputStream fos = null;
250
251 try {
252 fos = mFile.startWrite();
253 XmlSerializer out = new FastXmlSerializer();
254 out.setOutput(fos, "utf-8");
255 out.startDocument(null, true);
256 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
257 out.startTag(null, "compat-packages");
258
259 final IPackageManager pm = AppGlobals.getPackageManager();
260 final int screenLayout = mService.mConfiguration.screenLayout;
261 final Iterator<String> it = pkgs.iterator();
262 while (it.hasNext()) {
263 String pkg = it.next();
264 ApplicationInfo ai = null;
265 try {
266 ai = pm.getApplicationInfo(pkg, 0);
267 } catch (RemoteException e) {
268 }
269 if (ai == null) {
270 continue;
271 }
272 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, false);
273 if (info.alwaysSupportsScreen()) {
274 continue;
275 }
276 if (info.neverSupportsScreen()) {
277 continue;
278 }
279 out.startTag(null, "pkg");
280 out.attribute(null, "name", pkg);
281 out.endTag(null, "pkg");
282 }
283
284 out.endTag(null, "compat-packages");
285 out.endDocument();
286
287 mFile.finishWrite(fos);
288 } catch (java.io.IOException e1) {
289 Slog.w(TAG, "Error writing compat packages", e1);
290 if (fos != null) {
291 mFile.failWrite(fos);
292 }
293 }
294 }
295}