blob: 21b68095ef67f3dab67fcbc70b29132685a37219 [file] [log] [blame]
Alan Viveretteb6a25732017-11-21 14:49:24 -05001/*
2 * Copyright (C) 2018 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;
Alan Viveretteb6a25732017-11-21 14:49:24 -050018
19import android.annotation.UiThread;
Wale Ogunwale402de822018-03-22 10:03:06 -070020import android.content.ComponentName;
Alan Viveretteb6a25732017-11-21 14:49:24 -050021import android.content.Context;
22import android.content.res.Configuration;
23import android.os.Build;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.util.AtomicFile;
28import android.util.DisplayMetrics;
29import android.util.Slog;
30import android.util.Xml;
31
32import com.android.internal.util.FastXmlSerializer;
33
34import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36import org.xmlpull.v1.XmlSerializer;
37
38import java.io.File;
39import java.io.FileInputStream;
40import java.io.FileOutputStream;
41import java.nio.charset.StandardCharsets;
42import java.util.HashMap;
Wale Ogunwale402de822018-03-22 10:03:06 -070043import java.util.HashSet;
Alan Viveretteb6a25732017-11-21 14:49:24 -050044import java.util.Map;
45
46/**
47 * Manages warning dialogs shown during application lifecycle.
48 */
49class AppWarnings {
50 private static final String TAG = "AppWarnings";
51 private static final String CONFIG_FILE_NAME = "packages-warnings.xml";
52
53 public static final int FLAG_HIDE_DISPLAY_SIZE = 0x01;
54 public static final int FLAG_HIDE_COMPILE_SDK = 0x02;
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +000055 public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
Alan Viveretteb6a25732017-11-21 14:49:24 -050056
57 private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
58
Wale Ogunwale008163e2018-07-23 23:11:08 -070059 private final ActivityTaskManagerService mAtm;
Alan Viveretteb6a25732017-11-21 14:49:24 -050060 private final Context mUiContext;
Wale Ogunwale008163e2018-07-23 23:11:08 -070061 private final ConfigHandler mHandler;
Alan Viveretteb6a25732017-11-21 14:49:24 -050062 private final UiHandler mUiHandler;
63 private final AtomicFile mConfigFile;
64
65 private UnsupportedDisplaySizeDialog mUnsupportedDisplaySizeDialog;
66 private UnsupportedCompileSdkDialog mUnsupportedCompileSdkDialog;
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +000067 private DeprecatedTargetSdkVersionDialog mDeprecatedTargetSdkVersionDialog;
Alan Viveretteb6a25732017-11-21 14:49:24 -050068
Wale Ogunwale402de822018-03-22 10:03:06 -070069 /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
70 private HashSet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
71 new HashSet<>();
72
73 /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
74 void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
75 mAlwaysShowUnsupportedCompileSdkWarningActivities.add(activity);
76 }
77
Alan Viveretteb6a25732017-11-21 14:49:24 -050078 /**
79 * Creates a new warning dialog manager.
80 * <p>
81 * <strong>Note:</strong> Must be called from the ActivityManagerService thread.
82 *
Wale Ogunwale008163e2018-07-23 23:11:08 -070083 * @param atm
Alan Viveretteb6a25732017-11-21 14:49:24 -050084 * @param uiContext
Wale Ogunwale008163e2018-07-23 23:11:08 -070085 * @param handler
Alan Viveretteb6a25732017-11-21 14:49:24 -050086 * @param uiHandler
87 * @param systemDir
88 */
Wale Ogunwale008163e2018-07-23 23:11:08 -070089 public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
Alan Viveretteb6a25732017-11-21 14:49:24 -050090 Handler uiHandler, File systemDir) {
Wale Ogunwale008163e2018-07-23 23:11:08 -070091 mAtm = atm;
Alan Viveretteb6a25732017-11-21 14:49:24 -050092 mUiContext = uiContext;
Wale Ogunwale008163e2018-07-23 23:11:08 -070093 mHandler = new ConfigHandler(handler.getLooper());
Alan Viveretteb6a25732017-11-21 14:49:24 -050094 mUiHandler = new UiHandler(uiHandler.getLooper());
Dianne Hackborne17b4452018-01-10 13:15:40 -080095 mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
Alan Viveretteb6a25732017-11-21 14:49:24 -050096
97 readConfigFromFileAmsThread();
98 }
99
100 /**
101 * Shows the "unsupported display size" warning, if necessary.
102 *
103 * @param r activity record for which the warning may be displayed
104 */
105 public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
Wale Ogunwale008163e2018-07-23 23:11:08 -0700106 final Configuration globalConfig = mAtm.getGlobalConfiguration();
Alan Viveretteb6a25732017-11-21 14:49:24 -0500107 if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700108 && r.info.applicationInfo.requiresSmallestWidthDp
109 > globalConfig.smallestScreenWidthDp) {
Alan Viveretteb6a25732017-11-21 14:49:24 -0500110 mUiHandler.showUnsupportedDisplaySizeDialog(r);
111 }
112 }
113
114 /**
115 * Shows the "unsupported compile SDK" warning, if necessary.
116 *
117 * @param r activity record for which the warning may be displayed
118 */
119 public void showUnsupportedCompileSdkDialogIfNeeded(ActivityRecord r) {
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700120 if (r.info.applicationInfo.compileSdkVersion == 0
121 || r.info.applicationInfo.compileSdkVersionCodename == null) {
Alan Viveretteb6a25732017-11-21 14:49:24 -0500122 // We don't know enough about this package. Abort!
123 return;
124 }
125
Ian Pedowitz2333f7c2018-04-25 08:57:54 -0700126 // TODO(b/75318890): Need to move this to when the app actually crashes.
Siarhei Vishniakou2830dd72018-04-05 10:35:38 -0700127 if (/*ActivityManager.isRunningInTestHarness()
Wale Ogunwale8b19de92018-11-29 19:58:26 -0800128 &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(
129 r.mActivityComponent)) {
Siarhei Vishniakou2830dd72018-04-05 10:35:38 -0700130 // Don't show warning if we are running in a test harness and we don't have to always
131 // show for this activity.
132 return;
Wale Ogunwale402de822018-03-22 10:03:06 -0700133 }
134
Alan Viveretteb6a25732017-11-21 14:49:24 -0500135 // If the application was built against an pre-release SDK that's older than the current
136 // platform OR if the current platform is pre-release and older than the SDK against which
137 // the application was built OR both are pre-release with the same SDK_INT but different
138 // codenames (e.g. simultaneous pre-release development), then we're likely to run into
139 // compatibility issues. Warn the user and offer to check for an update.
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700140 final int compileSdk = r.info.applicationInfo.compileSdkVersion;
Siarhei Vishniakou2830dd72018-04-05 10:35:38 -0700141 final int platformSdk = Build.VERSION.SDK_INT;
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700142 final boolean isCompileSdkPreview =
143 !"REL".equals(r.info.applicationInfo.compileSdkVersionCodename);
Alan Viveretteb6a25732017-11-21 14:49:24 -0500144 final boolean isPlatformSdkPreview = !"REL".equals(Build.VERSION.CODENAME);
145 if ((isCompileSdkPreview && compileSdk < platformSdk)
146 || (isPlatformSdkPreview && platformSdk < compileSdk)
147 || (isCompileSdkPreview && isPlatformSdkPreview && platformSdk == compileSdk
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700148 && !Build.VERSION.CODENAME.equals(
149 r.info.applicationInfo.compileSdkVersionCodename))) {
Alan Viveretteb6a25732017-11-21 14:49:24 -0500150 mUiHandler.showUnsupportedCompileSdkDialog(r);
151 }
152 }
153
154 /**
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000155 * Shows the "deprecated target sdk" warning, if necessary.
156 *
157 * @param r activity record for which the warning may be displayed
158 */
159 public void showDeprecatedTargetDialogIfNeeded(ActivityRecord r) {
Andrii Kulianeceebbf2019-06-26 17:36:51 -0700160 if (r.info.applicationInfo.targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT) {
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000161 mUiHandler.showDeprecatedTargetDialog(r);
162 }
163 }
164
165 /**
Alan Viveretteb6a25732017-11-21 14:49:24 -0500166 * Called when an activity is being started.
167 *
168 * @param r record for the activity being started
169 */
170 public void onStartActivity(ActivityRecord r) {
171 showUnsupportedCompileSdkDialogIfNeeded(r);
172 showUnsupportedDisplaySizeDialogIfNeeded(r);
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000173 showDeprecatedTargetDialogIfNeeded(r);
Alan Viveretteb6a25732017-11-21 14:49:24 -0500174 }
175
176 /**
177 * Called when an activity was previously started and is being resumed.
178 *
179 * @param r record for the activity being resumed
180 */
181 public void onResumeActivity(ActivityRecord r) {
182 showUnsupportedDisplaySizeDialogIfNeeded(r);
183 }
184
185 /**
186 * Called by ActivityManagerService when package data has been cleared.
187 *
188 * @param name the package whose data has been cleared
189 */
190 public void onPackageDataCleared(String name) {
191 removePackageAndHideDialogs(name);
192 }
193
194 /**
195 * Called by ActivityManagerService when a package has been uninstalled.
196 *
197 * @param name the package that has been uninstalled
198 */
199 public void onPackageUninstalled(String name) {
200 removePackageAndHideDialogs(name);
201 }
202
203 /**
204 * Called by ActivityManagerService when the default display density has changed.
205 */
206 public void onDensityChanged() {
207 mUiHandler.hideUnsupportedDisplaySizeDialog();
208 }
209
210 /**
211 * Does what it says on the tin.
212 */
213 private void removePackageAndHideDialogs(String name) {
214 mUiHandler.hideDialogsForPackage(name);
215
216 synchronized (mPackageFlags) {
217 mPackageFlags.remove(name);
Wale Ogunwale008163e2018-07-23 23:11:08 -0700218 mHandler.scheduleWrite();
Alan Viveretteb6a25732017-11-21 14:49:24 -0500219 }
220 }
221
222 /**
223 * Hides the "unsupported display size" warning.
224 * <p>
225 * <strong>Note:</strong> Must be called on the UI thread.
226 */
227 @UiThread
228 private void hideUnsupportedDisplaySizeDialogUiThread() {
229 if (mUnsupportedDisplaySizeDialog != null) {
230 mUnsupportedDisplaySizeDialog.dismiss();
231 mUnsupportedDisplaySizeDialog = null;
232 }
233 }
234
235 /**
236 * Shows the "unsupported display size" warning for the given application.
237 * <p>
238 * <strong>Note:</strong> Must be called on the UI thread.
239 *
240 * @param ar record for the activity that triggered the warning
241 */
242 @UiThread
243 private void showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar) {
244 if (mUnsupportedDisplaySizeDialog != null) {
245 mUnsupportedDisplaySizeDialog.dismiss();
246 mUnsupportedDisplaySizeDialog = null;
247 }
248 if (ar != null && !hasPackageFlag(
249 ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) {
250 mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
251 AppWarnings.this, mUiContext, ar.info.applicationInfo);
252 mUnsupportedDisplaySizeDialog.show();
253 }
254 }
255
256 /**
257 * Shows the "unsupported compile SDK" warning for the given application.
258 * <p>
259 * <strong>Note:</strong> Must be called on the UI thread.
260 *
261 * @param ar record for the activity that triggered the warning
262 */
263 @UiThread
264 private void showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar) {
265 if (mUnsupportedCompileSdkDialog != null) {
266 mUnsupportedCompileSdkDialog.dismiss();
267 mUnsupportedCompileSdkDialog = null;
268 }
269 if (ar != null && !hasPackageFlag(
270 ar.packageName, FLAG_HIDE_COMPILE_SDK)) {
271 mUnsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog(
272 AppWarnings.this, mUiContext, ar.info.applicationInfo);
273 mUnsupportedCompileSdkDialog.show();
274 }
275 }
276
277 /**
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000278 * Shows the "deprecated target sdk version" warning for the given application.
279 * <p>
280 * <strong>Note:</strong> Must be called on the UI thread.
281 *
282 * @param ar record for the activity that triggered the warning
283 */
284 @UiThread
285 private void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) {
286 if (mDeprecatedTargetSdkVersionDialog != null) {
287 mDeprecatedTargetSdkVersionDialog.dismiss();
288 mDeprecatedTargetSdkVersionDialog = null;
289 }
290 if (ar != null && !hasPackageFlag(
291 ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {
292 mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(
293 AppWarnings.this, mUiContext, ar.info.applicationInfo);
294 mDeprecatedTargetSdkVersionDialog.show();
295 }
296 }
297
298 /**
Alan Viveretteb6a25732017-11-21 14:49:24 -0500299 * Dismisses all warnings for the given package.
300 * <p>
301 * <strong>Note:</strong> Must be called on the UI thread.
302 *
303 * @param name the package for which warnings should be dismissed, or {@code null} to dismiss
304 * all warnings
305 */
306 @UiThread
307 private void hideDialogsForPackageUiThread(String name) {
308 // Hides the "unsupported display" dialog if necessary.
309 if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals(
310 mUnsupportedDisplaySizeDialog.getPackageName()))) {
311 mUnsupportedDisplaySizeDialog.dismiss();
312 mUnsupportedDisplaySizeDialog = null;
313 }
314
315 // Hides the "unsupported compile SDK" dialog if necessary.
316 if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals(
317 mUnsupportedCompileSdkDialog.getPackageName()))) {
318 mUnsupportedCompileSdkDialog.dismiss();
319 mUnsupportedCompileSdkDialog = null;
320 }
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000321
322 // Hides the "deprecated target sdk version" dialog if necessary.
323 if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
324 mDeprecatedTargetSdkVersionDialog.getPackageName()))) {
325 mDeprecatedTargetSdkVersionDialog.dismiss();
326 mDeprecatedTargetSdkVersionDialog = null;
327 }
Alan Viveretteb6a25732017-11-21 14:49:24 -0500328 }
329
330 /**
331 * Returns the value of the flag for the given package.
332 *
333 * @param name the package from which to retrieve the flag
334 * @param flag the bitmask for the flag to retrieve
335 * @return {@code true} if the flag is enabled, {@code false} otherwise
336 */
337 boolean hasPackageFlag(String name, int flag) {
338 return (getPackageFlags(name) & flag) == flag;
339 }
340
341 /**
342 * Sets the flag for the given package to the specified value.
343 *
344 * @param name the package on which to set the flag
345 * @param flag the bitmask for flag to set
346 * @param enabled the value to set for the flag
347 */
348 void setPackageFlag(String name, int flag, boolean enabled) {
349 synchronized (mPackageFlags) {
350 final int curFlags = getPackageFlags(name);
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000351 final int newFlags = enabled ? (curFlags | flag) : (curFlags & ~flag);
Alan Viveretteb6a25732017-11-21 14:49:24 -0500352 if (curFlags != newFlags) {
353 if (newFlags != 0) {
354 mPackageFlags.put(name, newFlags);
355 } else {
356 mPackageFlags.remove(name);
357 }
Wale Ogunwale008163e2018-07-23 23:11:08 -0700358 mHandler.scheduleWrite();
Alan Viveretteb6a25732017-11-21 14:49:24 -0500359 }
360 }
361 }
362
363 /**
364 * Returns the bitmask of flags set for the specified package.
365 */
366 private int getPackageFlags(String name) {
367 synchronized (mPackageFlags) {
368 return mPackageFlags.getOrDefault(name, 0);
369 }
370 }
371
372 /**
373 * Handles messages on the system process UI thread.
374 */
375 private final class UiHandler extends Handler {
376 private static final int MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 1;
377 private static final int MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 2;
378 private static final int MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG = 3;
379 private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4;
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000380 private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5;
Alan Viveretteb6a25732017-11-21 14:49:24 -0500381
382 public UiHandler(Looper looper) {
383 super(looper, null, true);
384 }
385
386 @Override
387 public void handleMessage(Message msg) {
388 switch (msg.what) {
389 case MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
390 final ActivityRecord ar = (ActivityRecord) msg.obj;
391 showUnsupportedDisplaySizeDialogUiThread(ar);
392 } break;
393 case MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
394 hideUnsupportedDisplaySizeDialogUiThread();
395 } break;
396 case MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG: {
397 final ActivityRecord ar = (ActivityRecord) msg.obj;
398 showUnsupportedCompileSdkDialogUiThread(ar);
399 } break;
400 case MSG_HIDE_DIALOGS_FOR_PACKAGE: {
401 final String name = (String) msg.obj;
402 hideDialogsForPackageUiThread(name);
403 } break;
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000404 case MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG: {
405 final ActivityRecord ar = (ActivityRecord) msg.obj;
406 showDeprecatedTargetSdkDialogUiThread(ar);
407 } break;
Alan Viveretteb6a25732017-11-21 14:49:24 -0500408 }
409 }
410
411 public void showUnsupportedDisplaySizeDialog(ActivityRecord r) {
412 removeMessages(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
413 obtainMessage(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG, r).sendToTarget();
414 }
415
416 public void hideUnsupportedDisplaySizeDialog() {
417 removeMessages(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
418 sendEmptyMessage(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
419 }
420
421 public void showUnsupportedCompileSdkDialog(ActivityRecord r) {
422 removeMessages(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG);
423 obtainMessage(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG, r).sendToTarget();
424 }
425
Przemyslaw Szczepaniakce73c7e2018-01-22 11:00:41 +0000426 public void showDeprecatedTargetDialog(ActivityRecord r) {
427 removeMessages(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG);
428 obtainMessage(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG, r).sendToTarget();
429 }
430
Alan Viveretteb6a25732017-11-21 14:49:24 -0500431 public void hideDialogsForPackage(String name) {
432 obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, name).sendToTarget();
433 }
434 }
435
436 /**
Wale Ogunwale008163e2018-07-23 23:11:08 -0700437 * Handles messages on the ActivityTaskManagerService thread.
Alan Viveretteb6a25732017-11-21 14:49:24 -0500438 */
439 private final class ConfigHandler extends Handler {
Wale Ogunwale008163e2018-07-23 23:11:08 -0700440 private static final int MSG_WRITE = 1;
Alan Viveretteb6a25732017-11-21 14:49:24 -0500441
442 private static final int DELAY_MSG_WRITE = 10000;
443
444 public ConfigHandler(Looper looper) {
445 super(looper, null, true);
446 }
447
448 @Override
449 public void handleMessage(Message msg) {
450 switch (msg.what) {
451 case MSG_WRITE:
452 writeConfigToFileAmsThread();
453 break;
454 }
455 }
456
457 public void scheduleWrite() {
458 removeMessages(MSG_WRITE);
459 sendEmptyMessageDelayed(MSG_WRITE, DELAY_MSG_WRITE);
460 }
461 }
462
463 /**
464 * Writes the configuration file.
465 * <p>
466 * <strong>Note:</strong> Should be called from the ActivityManagerService thread unless you
467 * don't care where you're doing I/O operations. But you <i>do</i> care, don't you?
468 */
469 private void writeConfigToFileAmsThread() {
470 // Create a shallow copy so that we don't have to synchronize on config.
471 final HashMap<String, Integer> packageFlags;
472 synchronized (mPackageFlags) {
473 packageFlags = new HashMap<>(mPackageFlags);
474 }
475
476 FileOutputStream fos = null;
477 try {
478 fos = mConfigFile.startWrite();
479
480 final XmlSerializer out = new FastXmlSerializer();
481 out.setOutput(fos, StandardCharsets.UTF_8.name());
482 out.startDocument(null, true);
483 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
484 out.startTag(null, "packages");
485
486 for (Map.Entry<String, Integer> entry : packageFlags.entrySet()) {
487 String pkg = entry.getKey();
488 int mode = entry.getValue();
489 if (mode == 0) {
490 continue;
491 }
492 out.startTag(null, "package");
493 out.attribute(null, "name", pkg);
494 out.attribute(null, "flags", Integer.toString(mode));
495 out.endTag(null, "package");
496 }
497
498 out.endTag(null, "packages");
499 out.endDocument();
500
501 mConfigFile.finishWrite(fos);
502 } catch (java.io.IOException e1) {
503 Slog.w(TAG, "Error writing package metadata", e1);
504 if (fos != null) {
505 mConfigFile.failWrite(fos);
506 }
507 }
508 }
509
510 /**
511 * Reads the configuration file and populates the package flags.
512 * <p>
513 * <strong>Note:</strong> Must be called from the constructor (and thus on the
514 * ActivityManagerService thread) since we don't synchronize on config.
515 */
516 private void readConfigFromFileAmsThread() {
517 FileInputStream fis = null;
518
519 try {
520 fis = mConfigFile.openRead();
521
522 final XmlPullParser parser = Xml.newPullParser();
523 parser.setInput(fis, StandardCharsets.UTF_8.name());
524
525 int eventType = parser.getEventType();
526 while (eventType != XmlPullParser.START_TAG &&
527 eventType != XmlPullParser.END_DOCUMENT) {
528 eventType = parser.next();
529 }
530 if (eventType == XmlPullParser.END_DOCUMENT) {
531 return;
532 }
533
534 String tagName = parser.getName();
535 if ("packages".equals(tagName)) {
536 eventType = parser.next();
537 do {
538 if (eventType == XmlPullParser.START_TAG) {
539 tagName = parser.getName();
540 if (parser.getDepth() == 2) {
541 if ("package".equals(tagName)) {
542 final String name = parser.getAttributeValue(null, "name");
543 if (name != null) {
544 final String flags = parser.getAttributeValue(
545 null, "flags");
546 int flagsInt = 0;
547 if (flags != null) {
548 try {
549 flagsInt = Integer.parseInt(flags);
550 } catch (NumberFormatException e) {
551 }
552 }
553 mPackageFlags.put(name, flagsInt);
554 }
555 }
556 }
557 }
558 eventType = parser.next();
559 } while (eventType != XmlPullParser.END_DOCUMENT);
560 }
561 } catch (XmlPullParserException e) {
562 Slog.w(TAG, "Error reading package metadata", e);
563 } catch (java.io.IOException e) {
564 if (fis != null) Slog.w(TAG, "Error reading package metadata", e);
565 } finally {
566 if (fis != null) {
567 try {
568 fis.close();
569 } catch (java.io.IOException e1) {
570 }
571 }
572 }
573 }
574}