blob: c8f8e82bdb181a12d6618c55ba90513c613e489b [file] [log] [blame]
Craig Mautnerc2c0a612014-02-20 20:25:41 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070018
Wale Ogunwale59507092018-10-29 09:00:30 -070019import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
20import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
21import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
22import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
23import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
Wale Ogunwalee23149f2015-03-06 15:39:44 -080024
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070025import java.io.File;
26import java.io.FileInputStream;
27import java.io.FileOutputStream;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010028import java.nio.charset.StandardCharsets;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070029import java.util.HashMap;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070030import java.util.Iterator;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070031import java.util.Map;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070032
33import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070037import com.android.internal.util.FastXmlSerializer;
38
39import android.app.ActivityManager;
40import android.app.AppGlobals;
41import android.content.pm.ApplicationInfo;
42import android.content.pm.IPackageManager;
43import android.content.res.CompatibilityInfo;
Andrii Kulian1779e612016-10-12 21:58:25 -070044import android.content.res.Configuration;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070045import android.os.Handler;
Jeff Brown6f357d32014-01-15 20:40:55 -080046import android.os.Looper;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070047import android.os.Message;
48import android.os.RemoteException;
Dianne Hackborn39606a02012-07-31 17:54:35 -070049import android.util.AtomicFile;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070050import android.util.Slog;
51import android.util.Xml;
52
Dianne Hackbornbe4e6aa2013-06-07 13:25:29 -070053public final class CompatModePackages {
Wale Ogunwale98875612018-10-12 07:53:02 -070054 private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
Wale Ogunwale3ab9a272015-03-16 09:55:45 -070055 private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070056
Wale Ogunwale53783742018-09-16 10:21:51 -070057 private final ActivityTaskManagerService mService;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070058 private final AtomicFile mFile;
59
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070060 // Compatibility state: no longer ask user to select the mode.
Wale Ogunwale53783742018-09-16 10:21:51 -070061 private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070062 // Compatibility state: compatibility mode is enabled.
Wale Ogunwale53783742018-09-16 10:21:51 -070063 private static final int COMPAT_FLAG_ENABLED = 1<<1;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070064
65 private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070066
Wale Ogunwale98875612018-10-12 07:53:02 -070067 private static final int MSG_WRITE = 300;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070068
Jeff Brown6f357d32014-01-15 20:40:55 -080069 private final CompatHandler mHandler;
70
71 private final class CompatHandler extends Handler {
72 public CompatHandler(Looper looper) {
73 super(looper, null, true);
74 }
75
76 @Override
77 public void handleMessage(Message msg) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070078 switch (msg.what) {
79 case MSG_WRITE:
80 saveCompatModes();
81 break;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070082 }
83 }
84 };
85
Wale Ogunwale53783742018-09-16 10:21:51 -070086 public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070087 mService = service;
Dianne Hackborne17b4452018-01-10 13:15:40 -080088 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
Jeff Brown6f357d32014-01-15 20:40:55 -080089 mHandler = new CompatHandler(handler.getLooper());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070090
91 FileInputStream fis = null;
92 try {
93 fis = mFile.openRead();
94 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010095 parser.setInput(fis, StandardCharsets.UTF_8.name());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070096 int eventType = parser.getEventType();
Narayan Kamath2ac3cb72014-01-06 11:31:35 +000097 while (eventType != XmlPullParser.START_TAG &&
98 eventType != XmlPullParser.END_DOCUMENT) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070099 eventType = parser.next();
100 }
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000101 if (eventType == XmlPullParser.END_DOCUMENT) {
102 return;
103 }
104
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700105 String tagName = parser.getName();
106 if ("compat-packages".equals(tagName)) {
107 eventType = parser.next();
108 do {
109 if (eventType == XmlPullParser.START_TAG) {
110 tagName = parser.getName();
111 if (parser.getDepth() == 2) {
112 if ("pkg".equals(tagName)) {
113 String pkg = parser.getAttributeValue(null, "name");
114 if (pkg != null) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700115 String mode = parser.getAttributeValue(null, "mode");
116 int modeInt = 0;
117 if (mode != null) {
118 try {
119 modeInt = Integer.parseInt(mode);
120 } catch (NumberFormatException e) {
121 }
122 }
123 mPackages.put(pkg, modeInt);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700124 }
125 }
126 }
127 }
128 eventType = parser.next();
129 } while (eventType != XmlPullParser.END_DOCUMENT);
130 }
131 } catch (XmlPullParserException e) {
132 Slog.w(TAG, "Error reading compat-packages", e);
133 } catch (java.io.IOException e) {
134 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
135 } finally {
136 if (fis != null) {
137 try {
138 fis.close();
139 } catch (java.io.IOException e1) {
140 }
141 }
142 }
143 }
144
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700145 public HashMap<String, Integer> getPackages() {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700146 return mPackages;
147 }
148
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700149 private int getPackageFlags(String packageName) {
150 Integer flags = mPackages.get(packageName);
151 return flags != null ? flags : 0;
152 }
153
Alan Viverette7df9b452016-06-16 12:47:58 -0400154 public void handlePackageDataClearedLocked(String packageName) {
155 // User has explicitly asked to clear all associated data.
156 removePackage(packageName);
157 }
158
159 public void handlePackageUninstalledLocked(String packageName) {
160 // Clear settings when app is uninstalled since this is an explicit
161 // signal from the user to remove the app and all associated data.
162 removePackage(packageName);
163 }
164
165 private void removePackage(String packageName) {
166 if (mPackages.containsKey(packageName)) {
167 mPackages.remove(packageName);
168 scheduleWrite();
169 }
170 }
171
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700172 public void handlePackageAddedLocked(String packageName, boolean updated) {
173 ApplicationInfo ai = null;
174 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700175 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700176 } catch (RemoteException e) {
177 }
178 if (ai == null) {
179 return;
180 }
181 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
182 final boolean mayCompat = !ci.alwaysSupportsScreen()
183 && !ci.neverSupportsScreen();
184
Dianne Hackborna9551702011-06-14 21:05:03 -0700185 if (updated) {
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700186 // Update -- if the app no longer can run in compat mode, clear
187 // any current settings for it.
188 if (!mayCompat && mPackages.containsKey(packageName)) {
189 mPackages.remove(packageName);
Alan Viverette7df9b452016-06-16 12:47:58 -0400190 scheduleWrite();
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700191 }
192 }
193 }
194
Alan Viverette7df9b452016-06-16 12:47:58 -0400195 private void scheduleWrite() {
196 mHandler.removeMessages(MSG_WRITE);
197 Message msg = mHandler.obtainMessage(MSG_WRITE);
198 mHandler.sendMessageDelayed(msg, 10000);
199 }
200
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700201 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
Andrii Kulian1779e612016-10-12 21:58:25 -0700202 final Configuration globalConfig = mService.getGlobalConfiguration();
203 CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
204 globalConfig.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700205 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700206 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
207 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700208 }
209
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700210 public int computeCompatModeLocked(ApplicationInfo ai) {
Andrii Kulian1779e612016-10-12 21:58:25 -0700211 final boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
212 final Configuration globalConfig = mService.getGlobalConfiguration();
213 final CompatibilityInfo info = new CompatibilityInfo(ai, globalConfig.screenLayout,
214 globalConfig.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700215 if (info.alwaysSupportsScreen()) {
216 return ActivityManager.COMPAT_MODE_NEVER;
217 }
218 if (info.neverSupportsScreen()) {
219 return ActivityManager.COMPAT_MODE_ALWAYS;
220 }
221 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
222 : ActivityManager.COMPAT_MODE_DISABLED;
223 }
224
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700225 public boolean getPackageAskCompatModeLocked(String packageName) {
226 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
227 }
228
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700229 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
Alan Viveretteb6a25732017-11-21 14:49:24 -0500230 setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
Alan Viverette7df9b452016-06-16 12:47:58 -0400231 }
232
Alan Viveretteb6a25732017-11-21 14:49:24 -0500233 private void setPackageFlagLocked(String packageName, int flag, boolean set) {
Alan Viverette7df9b452016-06-16 12:47:58 -0400234 final int curFlags = getPackageFlags(packageName);
Alan Viveretteb6a25732017-11-21 14:49:24 -0500235 final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag);
Alan Viverette7df9b452016-06-16 12:47:58 -0400236 if (curFlags != newFlags) {
237 if (newFlags != 0) {
238 mPackages.put(packageName, newFlags);
239 } else {
240 mPackages.remove(packageName);
241 }
242 scheduleWrite();
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700243 }
244 }
245
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700246 public int getPackageScreenCompatModeLocked(String packageName) {
247 ApplicationInfo ai = null;
248 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700249 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700250 } catch (RemoteException e) {
251 }
252 if (ai == null) {
253 return ActivityManager.COMPAT_MODE_UNKNOWN;
254 }
255 return computeCompatModeLocked(ai);
256 }
257
258 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
259 ApplicationInfo ai = null;
260 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700261 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700262 } catch (RemoteException e) {
263 }
264 if (ai == null) {
265 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
266 return;
267 }
268 setPackageScreenCompatModeLocked(ai, mode);
269 }
270
Wale Ogunwalea6191b42018-05-09 07:41:32 -0700271 void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700272 final String packageName = ai.packageName;
273
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700274 int curFlags = getPackageFlags(packageName);
275
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700276 boolean enable;
277 switch (mode) {
278 case ActivityManager.COMPAT_MODE_DISABLED:
279 enable = false;
280 break;
281 case ActivityManager.COMPAT_MODE_ENABLED:
282 enable = true;
283 break;
284 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700285 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700286 break;
287 default:
288 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
289 return;
290 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700291
292 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700293 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700294 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700295 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700296 newFlags &= ~COMPAT_FLAG_ENABLED;
297 }
298
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700299 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
300 if (ci.alwaysSupportsScreen()) {
301 Slog.w(TAG, "Ignoring compat mode change of " + packageName
302 + "; compatibility never needed");
303 newFlags = 0;
304 }
305 if (ci.neverSupportsScreen()) {
306 Slog.w(TAG, "Ignoring compat mode change of " + packageName
307 + "; compatibility always needed");
308 newFlags = 0;
309 }
310
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700311 if (newFlags != curFlags) {
312 if (newFlags != 0) {
313 mPackages.put(packageName, newFlags);
314 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700315 mPackages.remove(packageName);
316 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700317
318 // Need to get compatibility info in new state.
319 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700320
Alan Viverette7df9b452016-06-16 12:47:58 -0400321 scheduleWrite();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700322
Wale Ogunwale53783742018-09-16 10:21:51 -0700323 final ActivityStack stack = mService.getTopDisplayFocusedStack();
Craig Mautner20e72272013-04-01 13:45:53 -0700324 ActivityRecord starting = stack.restartPackage(packageName);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700325
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700326 // Tell all processes that loaded this package about the change.
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700327 for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
328 final WindowProcessController app = mService.mPidMap.valueAt(i);
329 if (!app.mPkgList.contains(packageName)) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700330 continue;
331 }
332 try {
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700333 if (app.hasThread()) {
Wale Ogunwale3ab9a272015-03-16 09:55:45 -0700334 if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700335 + app.mName + " new compat " + ci);
336 app.getThread().updatePackageCompatibilityInfo(packageName, ci);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700337 }
338 } catch (Exception e) {
339 }
340 }
341
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700342 if (starting != null) {
Wale Ogunwaleb6d75f32018-02-22 20:44:56 -0800343 starting.ensureActivityConfiguration(0 /* globalChanges */,
Andrii Kulian21713ac2016-10-12 22:05:05 -0700344 false /* preserveWindow */);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700345 // And we need to make sure at this point that all other activities
346 // are made visible with the correct configuration.
Filip Gruszczynskibc5a6c52015-09-22 13:13:24 -0700347 stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700348 }
349 }
350 }
351
Wale Ogunwale53783742018-09-16 10:21:51 -0700352 private void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700353 HashMap<String, Integer> pkgs;
Wale Ogunwale53783742018-09-16 10:21:51 -0700354 synchronized (mService.mGlobalLock) {
355 pkgs = new HashMap<>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700356 }
357
358 FileOutputStream fos = null;
359
360 try {
361 fos = mFile.startWrite();
362 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100363 out.setOutput(fos, StandardCharsets.UTF_8.name());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700364 out.startDocument(null, true);
365 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
366 out.startTag(null, "compat-packages");
367
368 final IPackageManager pm = AppGlobals.getPackageManager();
Andrii Kulian1779e612016-10-12 21:58:25 -0700369 final Configuration globalConfig = mService.getGlobalConfiguration();
370 final int screenLayout = globalConfig.screenLayout;
371 final int smallestScreenWidthDp = globalConfig.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700372 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700373 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700374 Map.Entry<String, Integer> entry = it.next();
375 String pkg = entry.getKey();
376 int mode = entry.getValue();
377 if (mode == 0) {
378 continue;
379 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700380 ApplicationInfo ai = null;
381 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700382 ai = pm.getApplicationInfo(pkg, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700383 } catch (RemoteException e) {
384 }
385 if (ai == null) {
386 continue;
387 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700388 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
389 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700390 if (info.alwaysSupportsScreen()) {
391 continue;
392 }
393 if (info.neverSupportsScreen()) {
394 continue;
395 }
396 out.startTag(null, "pkg");
397 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700398 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700399 out.endTag(null, "pkg");
400 }
401
402 out.endTag(null, "compat-packages");
403 out.endDocument();
404
405 mFile.finishWrite(fos);
406 } catch (java.io.IOException e1) {
407 Slog.w(TAG, "Error writing compat packages", e1);
408 if (fos != null) {
409 mFile.failWrite(fos);
410 }
411 }
412 }
413}