1 /* 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 17 package com.android.server.wm; 18 19 import android.annotation.NonNull; 20 import android.annotation.UiThread; 21 import android.annotation.WorkerThread; 22 import android.app.AlertDialog; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.ApplicationInfo; 29 import android.content.res.Configuration; 30 import android.os.Build; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.SystemProperties; 35 import android.util.ArrayMap; 36 import android.util.ArraySet; 37 import android.util.AtomicFile; 38 import android.util.DisplayMetrics; 39 import android.util.Slog; 40 import android.util.Xml; 41 42 import com.android.internal.annotations.GuardedBy; 43 import com.android.internal.util.ArrayUtils; 44 import com.android.modules.utils.TypedXmlPullParser; 45 import com.android.modules.utils.TypedXmlSerializer; 46 import com.android.server.IoThread; 47 48 import org.xmlpull.v1.XmlPullParser; 49 import org.xmlpull.v1.XmlPullParserException; 50 51 import java.io.File; 52 import java.io.FileInputStream; 53 import java.io.FileOutputStream; 54 import java.util.concurrent.atomic.AtomicReference; 55 56 /** 57 * Manages warning dialogs shown during application lifecycle. 58 */ 59 class AppWarnings { 60 private static final String TAG = "AppWarnings"; 61 private static final String CONFIG_FILE_NAME = "packages-warnings.xml"; 62 63 public static final int FLAG_HIDE_DISPLAY_SIZE = 0x01; 64 public static final int FLAG_HIDE_COMPILE_SDK = 0x02; 65 public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04; 66 public static final int FLAG_HIDE_DEPRECATED_ABI = 0x08; 67 68 @GuardedBy("mPackageFlags") 69 private final ArrayMap<String, Integer> mPackageFlags = new ArrayMap<>(); 70 71 private final ActivityTaskManagerService mAtm; 72 private final Context mUiContext; 73 private final WriteConfigTask mWriteConfigTask; 74 private final UiHandler mUiHandler; 75 private final AtomicFile mConfigFile; 76 77 private UnsupportedDisplaySizeDialog mUnsupportedDisplaySizeDialog; 78 private UnsupportedCompileSdkDialog mUnsupportedCompileSdkDialog; 79 private DeprecatedTargetSdkVersionDialog mDeprecatedTargetSdkVersionDialog; 80 private DeprecatedAbiDialog mDeprecatedAbiDialog; 81 82 /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */ 83 private final ArraySet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities = 84 new ArraySet<>(); 85 86 /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */ alwaysShowUnsupportedCompileSdkWarning(ComponentName activity)87 void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) { 88 mAlwaysShowUnsupportedCompileSdkWarningActivities.add(activity); 89 } 90 91 /** Creates a new warning dialog manager. */ AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler, Handler uiHandler, File systemDir)92 public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler, 93 Handler uiHandler, File systemDir) { 94 mAtm = atm; 95 mUiContext = uiContext; 96 mWriteConfigTask = new WriteConfigTask(); 97 mUiHandler = new UiHandler(uiHandler.getLooper()); 98 mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config"); 99 100 readConfigFromFileAmsThread(); 101 } 102 103 /** 104 * Shows the "unsupported display size" warning, if necessary. 105 * 106 * @param r activity record for which the warning may be displayed 107 */ showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r)108 public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) { 109 final Configuration globalConfig = mAtm.getGlobalConfiguration(); 110 if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE 111 && r.info.applicationInfo.requiresSmallestWidthDp 112 > globalConfig.smallestScreenWidthDp) { 113 mUiHandler.showUnsupportedDisplaySizeDialog(r); 114 } 115 } 116 117 /** 118 * Shows the "unsupported compile SDK" warning, if necessary. 119 * 120 * @param r activity record for which the warning may be displayed 121 */ showUnsupportedCompileSdkDialogIfNeeded(ActivityRecord r)122 public void showUnsupportedCompileSdkDialogIfNeeded(ActivityRecord r) { 123 if (r.info.applicationInfo.compileSdkVersion == 0 124 || r.info.applicationInfo.compileSdkVersionCodename == null) { 125 // We don't know enough about this package. Abort! 126 return; 127 } 128 129 // TODO(b/75318890): Need to move this to when the app actually crashes. 130 if (/*ActivityManager.isRunningInTestHarness() 131 &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains( 132 r.mActivityComponent)) { 133 // Don't show warning if we are running in a test harness and we don't have to always 134 // show for this activity. 135 return; 136 } 137 138 // If the application was built against an pre-release SDK that's older than the current 139 // platform OR if the current platform is pre-release and older than the SDK against which 140 // the application was built OR both are pre-release with the same SDK_INT but different 141 // codenames (e.g. simultaneous pre-release development), then we're likely to run into 142 // compatibility issues. Warn the user and offer to check for an update. 143 final int compileSdk = r.info.applicationInfo.compileSdkVersion; 144 final int platformSdk = Build.VERSION.SDK_INT; 145 final boolean isCompileSdkPreview = 146 !"REL".equals(r.info.applicationInfo.compileSdkVersionCodename); 147 final boolean isPlatformSdkPreview = !"REL".equals(Build.VERSION.CODENAME); 148 if ((isCompileSdkPreview && compileSdk < platformSdk) 149 || (isPlatformSdkPreview && platformSdk < compileSdk) 150 || (isCompileSdkPreview && isPlatformSdkPreview && platformSdk == compileSdk 151 && !Build.VERSION.CODENAME.equals( 152 r.info.applicationInfo.compileSdkVersionCodename))) { 153 mUiHandler.showUnsupportedCompileSdkDialog(r); 154 } 155 } 156 157 /** 158 * Shows the "deprecated target sdk" warning, if necessary. 159 * 160 * @param r activity record for which the warning may be displayed 161 */ showDeprecatedTargetDialogIfNeeded(ActivityRecord r)162 public void showDeprecatedTargetDialogIfNeeded(ActivityRecord r) { 163 if (r.info.applicationInfo.targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT) { 164 mUiHandler.showDeprecatedTargetDialog(r); 165 } 166 } 167 168 /** 169 * Shows the "deprecated abi" warning, if necessary. This can only happen is the device 170 * supports both 64-bit and 32-bit ABIs, and the app only contains 32-bit libraries. The app 171 * cannot be installed if the device only supports 64-bit ABI while the app contains only 32-bit 172 * libraries. 173 * 174 * @param r activity record for which the warning may be displayed 175 */ showDeprecatedAbiDialogIfNeeded(ActivityRecord r)176 public void showDeprecatedAbiDialogIfNeeded(ActivityRecord r) { 177 final boolean isUsingAbiOverride = (r.info.applicationInfo.privateFlagsExt 178 & ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE) != 0; 179 if (isUsingAbiOverride) { 180 // The abiOverride flag was specified during installation, which means that if the app 181 // is currently running in 32-bit mode, it is intended. Do not show the warning dialog. 182 return; 183 } 184 // The warning dialog can also be disabled for debugging purpose 185 final boolean disableDeprecatedAbiDialog = SystemProperties.getBoolean( 186 "debug.wm.disable_deprecated_abi_dialog", false); 187 if (disableDeprecatedAbiDialog) { 188 return; 189 } 190 final String appPrimaryAbi = r.info.applicationInfo.primaryCpuAbi; 191 final String appSecondaryAbi = r.info.applicationInfo.secondaryCpuAbi; 192 final boolean appContainsOnly32bitLibraries = 193 (appPrimaryAbi != null && appSecondaryAbi == null && !appPrimaryAbi.contains("64")); 194 final boolean is64BitDevice = 195 ArrayUtils.find(Build.SUPPORTED_ABIS, abi -> abi.contains("64")) != null; 196 if (is64BitDevice && appContainsOnly32bitLibraries) { 197 mUiHandler.showDeprecatedAbiDialog(r); 198 } 199 } 200 201 /** 202 * Called when an activity is being started. 203 * 204 * @param r record for the activity being started 205 */ onStartActivity(ActivityRecord r)206 public void onStartActivity(ActivityRecord r) { 207 showUnsupportedCompileSdkDialogIfNeeded(r); 208 showUnsupportedDisplaySizeDialogIfNeeded(r); 209 showDeprecatedTargetDialogIfNeeded(r); 210 showDeprecatedAbiDialogIfNeeded(r); 211 } 212 213 /** 214 * Called when an activity was previously started and is being resumed. 215 * 216 * @param r record for the activity being resumed 217 */ onResumeActivity(ActivityRecord r)218 public void onResumeActivity(ActivityRecord r) { 219 showUnsupportedDisplaySizeDialogIfNeeded(r); 220 } 221 222 /** 223 * Called by ActivityManagerService when package data has been cleared. 224 * 225 * @param name the package whose data has been cleared 226 */ onPackageDataCleared(String name)227 public void onPackageDataCleared(String name) { 228 removePackageAndHideDialogs(name); 229 } 230 231 /** 232 * Called by ActivityManagerService when a package has been uninstalled. 233 * 234 * @param name the package that has been uninstalled 235 */ onPackageUninstalled(String name)236 public void onPackageUninstalled(String name) { 237 removePackageAndHideDialogs(name); 238 } 239 240 /** 241 * Called by ActivityManagerService when the default display density has changed. 242 */ onDensityChanged()243 public void onDensityChanged() { 244 mUiHandler.hideUnsupportedDisplaySizeDialog(); 245 } 246 247 /** 248 * Does what it says on the tin. 249 */ removePackageAndHideDialogs(String name)250 private void removePackageAndHideDialogs(String name) { 251 mUiHandler.hideDialogsForPackage(name); 252 253 synchronized (mPackageFlags) { 254 if (mPackageFlags.remove(name) != null) { 255 mWriteConfigTask.schedule(); 256 } 257 } 258 } 259 260 /** 261 * Hides the "unsupported display size" warning. 262 * <p> 263 * <strong>Note:</strong> Must be called on the UI thread. 264 */ 265 @UiThread hideUnsupportedDisplaySizeDialogUiThread()266 private void hideUnsupportedDisplaySizeDialogUiThread() { 267 if (mUnsupportedDisplaySizeDialog != null) { 268 mUnsupportedDisplaySizeDialog.dismiss(); 269 mUnsupportedDisplaySizeDialog = null; 270 } 271 } 272 273 /** 274 * Shows the "unsupported display size" warning for the given application. 275 * <p> 276 * <strong>Note:</strong> Must be called on the UI thread. 277 * 278 * @param ar record for the activity that triggered the warning 279 */ 280 @UiThread showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar)281 private void showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar) { 282 if (mUnsupportedDisplaySizeDialog != null) { 283 mUnsupportedDisplaySizeDialog.dismiss(); 284 mUnsupportedDisplaySizeDialog = null; 285 } 286 if (ar != null && !hasPackageFlag( 287 ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) { 288 mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog( 289 AppWarnings.this, mUiContext, ar.info.applicationInfo); 290 mUnsupportedDisplaySizeDialog.show(); 291 } 292 } 293 294 /** 295 * Shows the "unsupported compile SDK" warning for the given application. 296 * <p> 297 * <strong>Note:</strong> Must be called on the UI thread. 298 * 299 * @param ar record for the activity that triggered the warning 300 */ 301 @UiThread showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar)302 private void showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar) { 303 if (mUnsupportedCompileSdkDialog != null) { 304 mUnsupportedCompileSdkDialog.dismiss(); 305 mUnsupportedCompileSdkDialog = null; 306 } 307 if (ar != null && !hasPackageFlag( 308 ar.packageName, FLAG_HIDE_COMPILE_SDK)) { 309 mUnsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog( 310 AppWarnings.this, mUiContext, ar.info.applicationInfo); 311 mUnsupportedCompileSdkDialog.show(); 312 } 313 } 314 315 /** 316 * Shows the "deprecated target sdk version" warning for the given application. 317 * <p> 318 * <strong>Note:</strong> Must be called on the UI thread. 319 * 320 * @param ar record for the activity that triggered the warning 321 */ 322 @UiThread showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar)323 private void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) { 324 if (mDeprecatedTargetSdkVersionDialog != null) { 325 mDeprecatedTargetSdkVersionDialog.dismiss(); 326 mDeprecatedTargetSdkVersionDialog = null; 327 } 328 if (ar != null && !hasPackageFlag( 329 ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) { 330 mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog( 331 AppWarnings.this, mUiContext, ar.info.applicationInfo); 332 mDeprecatedTargetSdkVersionDialog.show(); 333 } 334 } 335 336 /** 337 * Shows the "deprecated abi" warning for the given application. 338 * <p> 339 * <strong>Note:</strong> Must be called on the UI thread. 340 * 341 * @param ar record for the activity that triggered the warning 342 */ 343 @UiThread showDeprecatedAbiDialogUiThread(ActivityRecord ar)344 private void showDeprecatedAbiDialogUiThread(ActivityRecord ar) { 345 if (mDeprecatedAbiDialog != null) { 346 mDeprecatedAbiDialog.dismiss(); 347 mDeprecatedAbiDialog = null; 348 } 349 if (ar != null && !hasPackageFlag( 350 ar.packageName, FLAG_HIDE_DEPRECATED_ABI)) { 351 mDeprecatedAbiDialog = new DeprecatedAbiDialog( 352 AppWarnings.this, mUiContext, ar.info.applicationInfo); 353 mDeprecatedAbiDialog.show(); 354 } 355 } 356 357 /** 358 * Dismisses all warnings for the given package. 359 * <p> 360 * <strong>Note:</strong> Must be called on the UI thread. 361 * 362 * @param name the package for which warnings should be dismissed, or {@code null} to dismiss 363 * all warnings 364 */ 365 @UiThread hideDialogsForPackageUiThread(String name)366 private void hideDialogsForPackageUiThread(String name) { 367 // Hides the "unsupported display" dialog if necessary. 368 if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals( 369 mUnsupportedDisplaySizeDialog.mPackageName))) { 370 mUnsupportedDisplaySizeDialog.dismiss(); 371 mUnsupportedDisplaySizeDialog = null; 372 } 373 374 // Hides the "unsupported compile SDK" dialog if necessary. 375 if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals( 376 mUnsupportedCompileSdkDialog.mPackageName))) { 377 mUnsupportedCompileSdkDialog.dismiss(); 378 mUnsupportedCompileSdkDialog = null; 379 } 380 381 // Hides the "deprecated target sdk version" dialog if necessary. 382 if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals( 383 mDeprecatedTargetSdkVersionDialog.mPackageName))) { 384 mDeprecatedTargetSdkVersionDialog.dismiss(); 385 mDeprecatedTargetSdkVersionDialog = null; 386 } 387 388 // Hides the "deprecated abi" dialog if necessary. 389 if (mDeprecatedAbiDialog != null && (name == null || name.equals( 390 mDeprecatedAbiDialog.mPackageName))) { 391 mDeprecatedAbiDialog.dismiss(); 392 mDeprecatedAbiDialog = null; 393 } 394 } 395 396 /** 397 * Returns the value of the flag for the given package. 398 * 399 * @param name the package from which to retrieve the flag 400 * @param flag the bitmask for the flag to retrieve 401 * @return {@code true} if the flag is enabled, {@code false} otherwise 402 */ hasPackageFlag(String name, int flag)403 boolean hasPackageFlag(String name, int flag) { 404 return (getPackageFlags(name) & flag) == flag; 405 } 406 407 /** 408 * Sets the flag for the given package to the specified value. 409 * 410 * @param name the package on which to set the flag 411 * @param flag the bitmask for flag to set 412 * @param enabled the value to set for the flag 413 */ setPackageFlag(String name, int flag, boolean enabled)414 void setPackageFlag(String name, int flag, boolean enabled) { 415 synchronized (mPackageFlags) { 416 final int curFlags = getPackageFlags(name); 417 final int newFlags = enabled ? (curFlags | flag) : (curFlags & ~flag); 418 if (curFlags != newFlags) { 419 if (newFlags != 0) { 420 mPackageFlags.put(name, newFlags); 421 } else { 422 mPackageFlags.remove(name); 423 } 424 mWriteConfigTask.schedule(); 425 } 426 } 427 } 428 429 /** 430 * Returns the bitmask of flags set for the specified package. 431 */ getPackageFlags(String name)432 private int getPackageFlags(String name) { 433 synchronized (mPackageFlags) { 434 return mPackageFlags.getOrDefault(name, 0); 435 } 436 } 437 438 /** 439 * Handles messages on the system process UI thread. 440 */ 441 private final class UiHandler extends Handler { 442 private static final int MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 1; 443 private static final int MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 2; 444 private static final int MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG = 3; 445 private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4; 446 private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5; 447 private static final int MSG_SHOW_DEPRECATED_ABI_DIALOG = 6; 448 UiHandler(Looper looper)449 public UiHandler(Looper looper) { 450 super(looper, null, true); 451 } 452 453 @Override handleMessage(Message msg)454 public void handleMessage(Message msg) { 455 switch (msg.what) { 456 case MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG: { 457 final ActivityRecord ar = (ActivityRecord) msg.obj; 458 showUnsupportedDisplaySizeDialogUiThread(ar); 459 } break; 460 case MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG: { 461 hideUnsupportedDisplaySizeDialogUiThread(); 462 } break; 463 case MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG: { 464 final ActivityRecord ar = (ActivityRecord) msg.obj; 465 showUnsupportedCompileSdkDialogUiThread(ar); 466 } break; 467 case MSG_HIDE_DIALOGS_FOR_PACKAGE: { 468 final String name = (String) msg.obj; 469 hideDialogsForPackageUiThread(name); 470 } break; 471 case MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG: { 472 final ActivityRecord ar = (ActivityRecord) msg.obj; 473 showDeprecatedTargetSdkDialogUiThread(ar); 474 } break; 475 case MSG_SHOW_DEPRECATED_ABI_DIALOG: { 476 final ActivityRecord ar = (ActivityRecord) msg.obj; 477 showDeprecatedAbiDialogUiThread(ar); 478 } break; 479 } 480 } 481 showUnsupportedDisplaySizeDialog(ActivityRecord r)482 public void showUnsupportedDisplaySizeDialog(ActivityRecord r) { 483 removeMessages(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG); 484 obtainMessage(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG, r).sendToTarget(); 485 } 486 hideUnsupportedDisplaySizeDialog()487 public void hideUnsupportedDisplaySizeDialog() { 488 removeMessages(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG); 489 sendEmptyMessage(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG); 490 } 491 showUnsupportedCompileSdkDialog(ActivityRecord r)492 public void showUnsupportedCompileSdkDialog(ActivityRecord r) { 493 removeMessages(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG); 494 obtainMessage(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG, r).sendToTarget(); 495 } 496 showDeprecatedTargetDialog(ActivityRecord r)497 public void showDeprecatedTargetDialog(ActivityRecord r) { 498 removeMessages(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG); 499 obtainMessage(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG, r).sendToTarget(); 500 } 501 showDeprecatedAbiDialog(ActivityRecord r)502 public void showDeprecatedAbiDialog(ActivityRecord r) { 503 removeMessages(MSG_SHOW_DEPRECATED_ABI_DIALOG); 504 obtainMessage(MSG_SHOW_DEPRECATED_ABI_DIALOG, r).sendToTarget(); 505 } 506 hideDialogsForPackage(String name)507 public void hideDialogsForPackage(String name) { 508 obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, name).sendToTarget(); 509 } 510 } 511 512 static class BaseDialog { 513 final AppWarnings mManager; 514 final String mPackageName; 515 AlertDialog mDialog; 516 private BroadcastReceiver mCloseReceiver; 517 BaseDialog(AppWarnings manager, String packageName)518 BaseDialog(AppWarnings manager, String packageName) { 519 mManager = manager; 520 mPackageName = packageName; 521 } 522 523 @UiThread show()524 void show() { 525 if (mDialog == null) return; 526 if (mCloseReceiver == null) { 527 mCloseReceiver = new BroadcastReceiver() { 528 @Override 529 public void onReceive(Context context, Intent intent) { 530 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 531 mManager.mUiHandler.hideDialogsForPackage(mPackageName); 532 } 533 } 534 }; 535 mManager.mUiContext.registerReceiver(mCloseReceiver, 536 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), 537 Context.RECEIVER_EXPORTED); 538 } 539 Slog.w(TAG, "Showing " + getClass().getSimpleName() + " for package " + mPackageName); 540 mDialog.show(); 541 } 542 543 @UiThread dismiss()544 void dismiss() { 545 if (mDialog == null) return; 546 if (mCloseReceiver != null) { 547 mManager.mUiContext.unregisterReceiver(mCloseReceiver); 548 mCloseReceiver = null; 549 } 550 mDialog.dismiss(); 551 mDialog = null; 552 } 553 } 554 555 private final class WriteConfigTask implements Runnable { 556 private static final long WRITE_CONFIG_DELAY_MS = 10000; 557 final AtomicReference<ArrayMap<String, Integer>> mPendingPackageFlags = 558 new AtomicReference<>(); 559 560 @Override run()561 public void run() { 562 final ArrayMap<String, Integer> packageFlags = mPendingPackageFlags.getAndSet(null); 563 if (packageFlags != null) { 564 writeConfigToFile(packageFlags); 565 } 566 } 567 568 @GuardedBy("mPackageFlags") schedule()569 void schedule() { 570 if (mPendingPackageFlags.getAndSet(new ArrayMap<>(mPackageFlags)) == null) { 571 IoThread.getHandler().postDelayed(this, WRITE_CONFIG_DELAY_MS); 572 } 573 } 574 } 575 576 /** Writes the configuration file. */ 577 @WorkerThread writeConfigToFile(@onNull ArrayMap<String, Integer> packageFlags)578 private void writeConfigToFile(@NonNull ArrayMap<String, Integer> packageFlags) { 579 FileOutputStream fos = null; 580 try { 581 fos = mConfigFile.startWrite(); 582 583 final TypedXmlSerializer out = Xml.resolveSerializer(fos); 584 out.startDocument(null, true); 585 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 586 out.startTag(null, "packages"); 587 588 for (int i = 0; i < packageFlags.size(); i++) { 589 final String pkg = packageFlags.keyAt(i); 590 final int mode = packageFlags.valueAt(i); 591 if (mode == 0) { 592 continue; 593 } 594 out.startTag(null, "package"); 595 out.attribute(null, "name", pkg); 596 out.attributeInt(null, "flags", mode); 597 out.endTag(null, "package"); 598 } 599 600 out.endTag(null, "packages"); 601 out.endDocument(); 602 603 mConfigFile.finishWrite(fos); 604 } catch (java.io.IOException e1) { 605 Slog.w(TAG, "Error writing package metadata", e1); 606 if (fos != null) { 607 mConfigFile.failWrite(fos); 608 } 609 } 610 } 611 612 /** 613 * Reads the configuration file and populates the package flags. 614 * <p> 615 * <strong>Note:</strong> Must be called from the constructor (and thus on the 616 * ActivityManagerService thread) since we don't synchronize on config. 617 */ readConfigFromFileAmsThread()618 private void readConfigFromFileAmsThread() { 619 FileInputStream fis = null; 620 621 try { 622 fis = mConfigFile.openRead(); 623 624 final TypedXmlPullParser parser = Xml.resolvePullParser(fis); 625 626 int eventType = parser.getEventType(); 627 while (eventType != XmlPullParser.START_TAG && 628 eventType != XmlPullParser.END_DOCUMENT) { 629 eventType = parser.next(); 630 } 631 if (eventType == XmlPullParser.END_DOCUMENT) { 632 return; 633 } 634 635 String tagName = parser.getName(); 636 if ("packages".equals(tagName)) { 637 eventType = parser.next(); 638 do { 639 if (eventType == XmlPullParser.START_TAG) { 640 tagName = parser.getName(); 641 if (parser.getDepth() == 2) { 642 if ("package".equals(tagName)) { 643 final String name = parser.getAttributeValue(null, "name"); 644 if (name != null) { 645 int flagsInt = parser.getAttributeInt(null, "flags", 0); 646 mPackageFlags.put(name, flagsInt); 647 } 648 } 649 } 650 } 651 eventType = parser.next(); 652 } while (eventType != XmlPullParser.END_DOCUMENT); 653 } 654 } catch (XmlPullParserException e) { 655 Slog.w(TAG, "Error reading package metadata", e); 656 } catch (java.io.IOException e) { 657 if (fis != null) Slog.w(TAG, "Error reading package metadata", e); 658 } finally { 659 if (fis != null) { 660 try { 661 fis.close(); 662 } catch (java.io.IOException e1) { 663 } 664 } 665 } 666 } 667 } 668