blob: 320ca65d215b19ebd92b0d375f1225f7cf45c007 [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;
Michal Karpinski2e0aad22019-04-12 16:22:55 +010051import android.util.SparseArray;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070052import android.util.Xml;
53
Dianne Hackbornbe4e6aa2013-06-07 13:25:29 -070054public final class CompatModePackages {
Wale Ogunwale98875612018-10-12 07:53:02 -070055 private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
Wale Ogunwale3ab9a272015-03-16 09:55:45 -070056 private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070057
Wale Ogunwale53783742018-09-16 10:21:51 -070058 private final ActivityTaskManagerService mService;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070059 private final AtomicFile mFile;
60
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070061 // Compatibility state: no longer ask user to select the mode.
Wale Ogunwale53783742018-09-16 10:21:51 -070062 private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070063 // Compatibility state: compatibility mode is enabled.
Wale Ogunwale53783742018-09-16 10:21:51 -070064 private static final int COMPAT_FLAG_ENABLED = 1<<1;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -070065
66 private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070067
Wale Ogunwale98875612018-10-12 07:53:02 -070068 private static final int MSG_WRITE = 300;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070069
Jeff Brown6f357d32014-01-15 20:40:55 -080070 private final CompatHandler mHandler;
71
72 private final class CompatHandler extends Handler {
73 public CompatHandler(Looper looper) {
74 super(looper, null, true);
75 }
76
77 @Override
78 public void handleMessage(Message msg) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070079 switch (msg.what) {
80 case MSG_WRITE:
81 saveCompatModes();
82 break;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070083 }
84 }
85 };
86
Wale Ogunwale53783742018-09-16 10:21:51 -070087 public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070088 mService = service;
Dianne Hackborne17b4452018-01-10 13:15:40 -080089 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
Jeff Brown6f357d32014-01-15 20:40:55 -080090 mHandler = new CompatHandler(handler.getLooper());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070091
92 FileInputStream fis = null;
93 try {
94 fis = mFile.openRead();
95 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010096 parser.setInput(fis, StandardCharsets.UTF_8.name());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -070097 int eventType = parser.getEventType();
Narayan Kamath2ac3cb72014-01-06 11:31:35 +000098 while (eventType != XmlPullParser.START_TAG &&
99 eventType != XmlPullParser.END_DOCUMENT) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700100 eventType = parser.next();
101 }
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000102 if (eventType == XmlPullParser.END_DOCUMENT) {
103 return;
104 }
105
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700106 String tagName = parser.getName();
107 if ("compat-packages".equals(tagName)) {
108 eventType = parser.next();
109 do {
110 if (eventType == XmlPullParser.START_TAG) {
111 tagName = parser.getName();
112 if (parser.getDepth() == 2) {
113 if ("pkg".equals(tagName)) {
114 String pkg = parser.getAttributeValue(null, "name");
115 if (pkg != null) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700116 String mode = parser.getAttributeValue(null, "mode");
117 int modeInt = 0;
118 if (mode != null) {
119 try {
120 modeInt = Integer.parseInt(mode);
121 } catch (NumberFormatException e) {
122 }
123 }
124 mPackages.put(pkg, modeInt);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700125 }
126 }
127 }
128 }
129 eventType = parser.next();
130 } while (eventType != XmlPullParser.END_DOCUMENT);
131 }
132 } catch (XmlPullParserException e) {
133 Slog.w(TAG, "Error reading compat-packages", e);
134 } catch (java.io.IOException e) {
135 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
136 } finally {
137 if (fis != null) {
138 try {
139 fis.close();
140 } catch (java.io.IOException e1) {
141 }
142 }
143 }
144 }
145
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700146 public HashMap<String, Integer> getPackages() {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700147 return mPackages;
148 }
149
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700150 private int getPackageFlags(String packageName) {
151 Integer flags = mPackages.get(packageName);
152 return flags != null ? flags : 0;
153 }
154
Alan Viverette7df9b452016-06-16 12:47:58 -0400155 public void handlePackageDataClearedLocked(String packageName) {
156 // User has explicitly asked to clear all associated data.
157 removePackage(packageName);
158 }
159
160 public void handlePackageUninstalledLocked(String packageName) {
161 // Clear settings when app is uninstalled since this is an explicit
162 // signal from the user to remove the app and all associated data.
163 removePackage(packageName);
164 }
165
166 private void removePackage(String packageName) {
167 if (mPackages.containsKey(packageName)) {
168 mPackages.remove(packageName);
169 scheduleWrite();
170 }
171 }
172
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700173 public void handlePackageAddedLocked(String packageName, boolean updated) {
174 ApplicationInfo ai = null;
175 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700176 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700177 } catch (RemoteException e) {
178 }
179 if (ai == null) {
180 return;
181 }
182 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
183 final boolean mayCompat = !ci.alwaysSupportsScreen()
184 && !ci.neverSupportsScreen();
185
Dianne Hackborna9551702011-06-14 21:05:03 -0700186 if (updated) {
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700187 // Update -- if the app no longer can run in compat mode, clear
188 // any current settings for it.
189 if (!mayCompat && mPackages.containsKey(packageName)) {
190 mPackages.remove(packageName);
Alan Viverette7df9b452016-06-16 12:47:58 -0400191 scheduleWrite();
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700192 }
193 }
194 }
195
Alan Viverette7df9b452016-06-16 12:47:58 -0400196 private void scheduleWrite() {
197 mHandler.removeMessages(MSG_WRITE);
198 Message msg = mHandler.obtainMessage(MSG_WRITE);
199 mHandler.sendMessageDelayed(msg, 10000);
200 }
201
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700202 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
Andrii Kulian1779e612016-10-12 21:58:25 -0700203 final Configuration globalConfig = mService.getGlobalConfiguration();
204 CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
205 globalConfig.smallestScreenWidthDp,
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700206 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
Dianne Hackborn2f0b1752011-05-31 17:59:49 -0700207 //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
208 return ci;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700209 }
210
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700211 public int computeCompatModeLocked(ApplicationInfo ai) {
Andrii Kulian1779e612016-10-12 21:58:25 -0700212 final boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
213 final Configuration globalConfig = mService.getGlobalConfiguration();
214 final CompatibilityInfo info = new CompatibilityInfo(ai, globalConfig.screenLayout,
215 globalConfig.smallestScreenWidthDp, enabled);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700216 if (info.alwaysSupportsScreen()) {
217 return ActivityManager.COMPAT_MODE_NEVER;
218 }
219 if (info.neverSupportsScreen()) {
220 return ActivityManager.COMPAT_MODE_ALWAYS;
221 }
222 return enabled ? ActivityManager.COMPAT_MODE_ENABLED
223 : ActivityManager.COMPAT_MODE_DISABLED;
224 }
225
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700226 public boolean getPackageAskCompatModeLocked(String packageName) {
227 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
228 }
229
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700230 public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
Alan Viveretteb6a25732017-11-21 14:49:24 -0500231 setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
Alan Viverette7df9b452016-06-16 12:47:58 -0400232 }
233
Alan Viveretteb6a25732017-11-21 14:49:24 -0500234 private void setPackageFlagLocked(String packageName, int flag, boolean set) {
Alan Viverette7df9b452016-06-16 12:47:58 -0400235 final int curFlags = getPackageFlags(packageName);
Alan Viveretteb6a25732017-11-21 14:49:24 -0500236 final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag);
Alan Viverette7df9b452016-06-16 12:47:58 -0400237 if (curFlags != newFlags) {
238 if (newFlags != 0) {
239 mPackages.put(packageName, newFlags);
240 } else {
241 mPackages.remove(packageName);
242 }
243 scheduleWrite();
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700244 }
245 }
246
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700247 public int getPackageScreenCompatModeLocked(String packageName) {
248 ApplicationInfo ai = null;
249 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700250 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700251 } catch (RemoteException e) {
252 }
253 if (ai == null) {
254 return ActivityManager.COMPAT_MODE_UNKNOWN;
255 }
256 return computeCompatModeLocked(ai);
257 }
258
259 public void setPackageScreenCompatModeLocked(String packageName, int mode) {
260 ApplicationInfo ai = null;
261 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700262 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700263 } catch (RemoteException e) {
264 }
265 if (ai == null) {
266 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
267 return;
268 }
269 setPackageScreenCompatModeLocked(ai, mode);
270 }
271
Wale Ogunwalea6191b42018-05-09 07:41:32 -0700272 void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700273 final String packageName = ai.packageName;
274
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700275 int curFlags = getPackageFlags(packageName);
276
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700277 boolean enable;
278 switch (mode) {
279 case ActivityManager.COMPAT_MODE_DISABLED:
280 enable = false;
281 break;
282 case ActivityManager.COMPAT_MODE_ENABLED:
283 enable = true;
284 break;
285 case ActivityManager.COMPAT_MODE_TOGGLE:
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700286 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700287 break;
288 default:
289 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
290 return;
291 }
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700292
293 int newFlags = curFlags;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700294 if (enable) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700295 newFlags |= COMPAT_FLAG_ENABLED;
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700296 } else {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700297 newFlags &= ~COMPAT_FLAG_ENABLED;
298 }
299
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700300 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
301 if (ci.alwaysSupportsScreen()) {
302 Slog.w(TAG, "Ignoring compat mode change of " + packageName
303 + "; compatibility never needed");
304 newFlags = 0;
305 }
306 if (ci.neverSupportsScreen()) {
307 Slog.w(TAG, "Ignoring compat mode change of " + packageName
308 + "; compatibility always needed");
309 newFlags = 0;
310 }
311
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700312 if (newFlags != curFlags) {
313 if (newFlags != 0) {
314 mPackages.put(packageName, newFlags);
315 } else {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700316 mPackages.remove(packageName);
317 }
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700318
319 // Need to get compatibility info in new state.
320 ci = compatibilityInfoForPackageLocked(ai);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700321
Alan Viverette7df9b452016-06-16 12:47:58 -0400322 scheduleWrite();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700323
Wale Ogunwale53783742018-09-16 10:21:51 -0700324 final ActivityStack stack = mService.getTopDisplayFocusedStack();
Craig Mautner20e72272013-04-01 13:45:53 -0700325 ActivityRecord starting = stack.restartPackage(packageName);
Dianne Hackborn8ea5e1d2011-05-27 16:45:31 -0700326
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700327 // Tell all processes that loaded this package about the change.
Michal Karpinski2e0aad22019-04-12 16:22:55 +0100328 SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
329 for (int i = pidMap.size() - 1; i >= 0; i--) {
330 final WindowProcessController app = pidMap.valueAt(i);
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700331 if (!app.mPkgList.contains(packageName)) {
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700332 continue;
333 }
334 try {
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700335 if (app.hasThread()) {
Wale Ogunwale3ab9a272015-03-16 09:55:45 -0700336 if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700337 + app.mName + " new compat " + ci);
338 app.getThread().updatePackageCompatibilityInfo(packageName, ci);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700339 }
340 } catch (Exception e) {
341 }
342 }
343
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700344 if (starting != null) {
Wale Ogunwaleb6d75f32018-02-22 20:44:56 -0800345 starting.ensureActivityConfiguration(0 /* globalChanges */,
Andrii Kulian21713ac2016-10-12 22:05:05 -0700346 false /* preserveWindow */);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700347 // And we need to make sure at this point that all other activities
348 // are made visible with the correct configuration.
Wale Ogunwale076c3b12019-11-20 12:17:22 -0800349 stack.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700350 }
351 }
352 }
353
Wale Ogunwale53783742018-09-16 10:21:51 -0700354 private void saveCompatModes() {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700355 HashMap<String, Integer> pkgs;
Wale Ogunwale53783742018-09-16 10:21:51 -0700356 synchronized (mService.mGlobalLock) {
357 pkgs = new HashMap<>(mPackages);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700358 }
359
360 FileOutputStream fos = null;
361
362 try {
363 fos = mFile.startWrite();
364 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100365 out.setOutput(fos, StandardCharsets.UTF_8.name());
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700366 out.startDocument(null, true);
367 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
368 out.startTag(null, "compat-packages");
369
370 final IPackageManager pm = AppGlobals.getPackageManager();
Andrii Kulian1779e612016-10-12 21:58:25 -0700371 final Configuration globalConfig = mService.getGlobalConfiguration();
372 final int screenLayout = globalConfig.screenLayout;
373 final int smallestScreenWidthDp = globalConfig.smallestScreenWidthDp;
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700374 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700375 while (it.hasNext()) {
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700376 Map.Entry<String, Integer> entry = it.next();
377 String pkg = entry.getKey();
378 int mode = entry.getValue();
379 if (mode == 0) {
380 continue;
381 }
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700382 ApplicationInfo ai = null;
383 try {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700384 ai = pm.getApplicationInfo(pkg, 0, 0);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700385 } catch (RemoteException e) {
386 }
387 if (ai == null) {
388 continue;
389 }
Dianne Hackborndf6e9802011-05-26 14:20:23 -0700390 CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
391 smallestScreenWidthDp, false);
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700392 if (info.alwaysSupportsScreen()) {
393 continue;
394 }
395 if (info.neverSupportsScreen()) {
396 continue;
397 }
398 out.startTag(null, "pkg");
399 out.attribute(null, "name", pkg);
Dianne Hackborn36cd41f2011-05-25 21:00:46 -0700400 out.attribute(null, "mode", Integer.toString(mode));
Dianne Hackborn0f1de9a2011-05-11 17:34:49 -0700401 out.endTag(null, "pkg");
402 }
403
404 out.endTag(null, "compat-packages");
405 out.endDocument();
406
407 mFile.finishWrite(fos);
408 } catch (java.io.IOException e1) {
409 Slog.w(TAG, "Error writing compat packages", e1);
410 if (fos != null) {
411 mFile.failWrite(fos);
412 }
413 }
414 }
415}