1 /* 2 * Copyright (C) 2017 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.settings.fuelgauge; 18 19 import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isUserConsumer; 20 21 import android.app.Activity; 22 import android.app.ActivityManager; 23 import android.app.backup.BackupManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.util.Log; 31 import android.view.View; 32 33 import androidx.annotation.VisibleForTesting; 34 35 import com.android.settings.R; 36 import com.android.settings.SettingsActivity; 37 import com.android.settings.Utils; 38 import com.android.settings.applications.appinfo.AppButtonsPreferenceController; 39 import com.android.settings.applications.appinfo.ButtonActionDialogFragment; 40 import com.android.settings.core.InstrumentedPreferenceFragment; 41 import com.android.settings.core.SubSettingLauncher; 42 import com.android.settings.dashboard.DashboardFragment; 43 import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; 44 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; 45 import com.android.settings.fuelgauge.batteryusage.BatteryEntry; 46 import com.android.settings.overlay.FeatureFactory; 47 import com.android.settings.widget.EntityHeaderController; 48 import com.android.settingslib.HelpUtils; 49 import com.android.settingslib.applications.AppUtils; 50 import com.android.settingslib.applications.ApplicationsState; 51 import com.android.settingslib.core.AbstractPreferenceController; 52 import com.android.settingslib.core.instrumentation.Instrumentable; 53 import com.android.settingslib.widget.FooterPreference; 54 import com.android.settingslib.widget.LayoutPreference; 55 import com.android.settingslib.widget.SelectorWithWidgetPreference; 56 57 import java.util.ArrayList; 58 import java.util.List; 59 import java.util.concurrent.ExecutorService; 60 import java.util.concurrent.Executors; 61 62 /** 63 * Power usage detail fragment for each app, this fragment contains 64 * 65 * 1. Detail battery usage information for app(i.e. usage time, usage amount) 66 * 2. Battery related controls for app(i.e uninstall, force stop) 67 */ 68 public class AdvancedPowerUsageDetail extends DashboardFragment implements 69 ButtonActionDialogFragment.AppButtonsDialogListener, 70 SelectorWithWidgetPreference.OnClickListener { 71 72 public static final String TAG = "AdvancedPowerDetail"; 73 public static final String EXTRA_UID = "extra_uid"; 74 public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; 75 public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time"; 76 public static final String EXTRA_BACKGROUND_TIME = "extra_background_time"; 77 public static final String EXTRA_SCREEN_ON_TIME = "extra_screen_on_time"; 78 public static final String EXTRA_SLOT_TIME = "extra_slot_time"; 79 public static final String EXTRA_LABEL = "extra_label"; 80 public static final String EXTRA_ICON_ID = "extra_icon_id"; 81 public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent"; 82 public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; 83 84 private static final String KEY_PREF_HEADER = "header_view"; 85 private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref"; 86 private static final String KEY_PREF_OPTIMIZED = "optimized_pref"; 87 private static final String KEY_PREF_RESTRICTED = "restricted_pref"; 88 private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; 89 private static final String PACKAGE_NAME_NONE = "none"; 90 91 private static final String HEADER_SUMMARY_FORMAT = "%s\n(%s)"; 92 93 private static final int REQUEST_UNINSTALL = 0; 94 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; 95 96 private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 97 98 @VisibleForTesting 99 LayoutPreference mHeaderPreference; 100 @VisibleForTesting 101 ApplicationsState mState; 102 @VisibleForTesting 103 ApplicationsState.AppEntry mAppEntry; 104 @VisibleForTesting 105 BatteryOptimizeUtils mBatteryOptimizeUtils; 106 @VisibleForTesting 107 FooterPreference mFooterPreference; 108 @VisibleForTesting 109 SelectorWithWidgetPreference mRestrictedPreference; 110 @VisibleForTesting 111 SelectorWithWidgetPreference mOptimizePreference; 112 @VisibleForTesting 113 SelectorWithWidgetPreference mUnrestrictedPreference; 114 @VisibleForTesting 115 @BatteryOptimizeUtils.OptimizationMode 116 int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; 117 @VisibleForTesting 118 BackupManager mBackupManager; 119 @VisibleForTesting 120 StringBuilder mLogStringBuilder; 121 122 private AppButtonsPreferenceController mAppButtonsPreferenceController; 123 124 // A wrapper class to carry LaunchBatteryDetailPage required arguments. 125 private static final class LaunchBatteryDetailPageArgs { 126 private String mUsagePercent; 127 private String mPackageName; 128 private String mAppLabel; 129 private String mSlotInformation; 130 private int mUid; 131 private int mIconId; 132 private int mConsumedPower; 133 private long mForegroundTimeMs; 134 private long mBackgroundTimeMs; 135 private long mScreenOnTimeMs; 136 private boolean mIsUserEntry; 137 } 138 139 /** Launches battery details page for an individual battery consumer. */ startBatteryDetailPage( Activity caller, InstrumentedPreferenceFragment fragment, BatteryDiffEntry diffEntry, String usagePercent, String slotInformation)140 public static void startBatteryDetailPage( 141 Activity caller, InstrumentedPreferenceFragment fragment, 142 BatteryDiffEntry diffEntry, String usagePercent, String slotInformation) { 143 startBatteryDetailPage( 144 caller, fragment.getMetricsCategory(), diffEntry, usagePercent, slotInformation, 145 /*showTimeInformation=*/ true); 146 } 147 148 /** Launches battery details page for an individual battery consumer fragment. */ startBatteryDetailPage( Context context, int sourceMetricsCategory, BatteryDiffEntry diffEntry, String usagePercent, String slotInformation, boolean showTimeInformation)149 public static void startBatteryDetailPage( 150 Context context, int sourceMetricsCategory, 151 BatteryDiffEntry diffEntry, String usagePercent, String slotInformation, 152 boolean showTimeInformation) { 153 final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs(); 154 // configure the launch argument. 155 launchArgs.mUsagePercent = usagePercent; 156 launchArgs.mPackageName = diffEntry.getPackageName(); 157 launchArgs.mAppLabel = diffEntry.getAppLabel(); 158 launchArgs.mSlotInformation = slotInformation; 159 launchArgs.mUid = (int) diffEntry.mUid; 160 launchArgs.mIconId = diffEntry.getAppIconId(); 161 launchArgs.mConsumedPower = (int) diffEntry.mConsumePower; 162 if (showTimeInformation) { 163 launchArgs.mForegroundTimeMs = diffEntry.mForegroundUsageTimeInMs; 164 launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs; 165 launchArgs.mScreenOnTimeMs = diffEntry.mScreenOnTimeInMs; 166 } 167 launchArgs.mIsUserEntry = isUserConsumer(diffEntry.mConsumerType); 168 startBatteryDetailPage(context, sourceMetricsCategory, launchArgs); 169 } 170 171 /** Launches battery details page for an individual battery consumer. */ startBatteryDetailPage(Activity caller, InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent)172 public static void startBatteryDetailPage(Activity caller, 173 InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent) { 174 final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs(); 175 // configure the launch argument. 176 launchArgs.mUsagePercent = usagePercent; 177 launchArgs.mPackageName = entry.getDefaultPackageName(); 178 launchArgs.mAppLabel = entry.getLabel(); 179 launchArgs.mUid = entry.getUid(); 180 launchArgs.mIconId = entry.mIconId; 181 launchArgs.mConsumedPower = (int) entry.getConsumedPower(); 182 launchArgs.mIsUserEntry = entry.isUserEntry(); 183 startBatteryDetailPage(caller, fragment.getMetricsCategory(), launchArgs); 184 } 185 startBatteryDetailPage( Context context, int sourceMetricsCategory, LaunchBatteryDetailPageArgs launchArgs)186 private static void startBatteryDetailPage( 187 Context context, int sourceMetricsCategory, LaunchBatteryDetailPageArgs launchArgs) { 188 final Bundle args = new Bundle(); 189 if (launchArgs.mPackageName == null) { 190 // populate data for system app 191 args.putString(EXTRA_LABEL, launchArgs.mAppLabel); 192 args.putInt(EXTRA_ICON_ID, launchArgs.mIconId); 193 args.putString(EXTRA_PACKAGE_NAME, null); 194 } else { 195 // populate data for normal app 196 args.putString(EXTRA_PACKAGE_NAME, launchArgs.mPackageName); 197 } 198 199 args.putInt(EXTRA_UID, launchArgs.mUid); 200 args.putLong(EXTRA_BACKGROUND_TIME, launchArgs.mBackgroundTimeMs); 201 args.putLong(EXTRA_FOREGROUND_TIME, launchArgs.mForegroundTimeMs); 202 args.putLong(EXTRA_SCREEN_ON_TIME, launchArgs.mScreenOnTimeMs); 203 args.putString(EXTRA_SLOT_TIME, launchArgs.mSlotInformation); 204 args.putString(EXTRA_POWER_USAGE_PERCENT, launchArgs.mUsagePercent); 205 args.putInt(EXTRA_POWER_USAGE_AMOUNT, launchArgs.mConsumedPower); 206 final int userId = launchArgs.mIsUserEntry ? ActivityManager.getCurrentUser() 207 : UserHandle.getUserId(launchArgs.mUid); 208 209 new SubSettingLauncher(context) 210 .setDestination(AdvancedPowerUsageDetail.class.getName()) 211 .setTitleRes(R.string.battery_details_title) 212 .setArguments(args) 213 .setSourceMetricsCategory(sourceMetricsCategory) 214 .setUserHandle(new UserHandle(userId)) 215 .launch(); 216 } 217 218 /** 219 * Start packageName's battery detail page. 220 */ startBatteryDetailPage( Activity caller, Instrumentable instrumentable, String packageName, UserHandle userHandle)221 public static void startBatteryDetailPage( 222 Activity caller, Instrumentable instrumentable, String packageName, 223 UserHandle userHandle) { 224 final Bundle args = new Bundle(3); 225 final PackageManager packageManager = caller.getPackageManager(); 226 args.putString(EXTRA_PACKAGE_NAME, packageName); 227 args.putString(EXTRA_POWER_USAGE_PERCENT, Utils.formatPercentage(0)); 228 try { 229 args.putInt(EXTRA_UID, packageManager.getPackageUid(packageName, 0 /* no flag */)); 230 } catch (PackageManager.NameNotFoundException e) { 231 Log.w(TAG, "Cannot find package: " + packageName, e); 232 } 233 234 new SubSettingLauncher(caller) 235 .setDestination(AdvancedPowerUsageDetail.class.getName()) 236 .setTitleRes(R.string.battery_details_title) 237 .setArguments(args) 238 .setSourceMetricsCategory(instrumentable.getMetricsCategory()) 239 .setUserHandle(userHandle) 240 .launch(); 241 } 242 243 @Override onAttach(Activity activity)244 public void onAttach(Activity activity) { 245 super.onAttach(activity); 246 247 mState = ApplicationsState.getInstance(getActivity().getApplication()); 248 } 249 250 @Override onCreate(Bundle icicle)251 public void onCreate(Bundle icicle) { 252 super.onCreate(icicle); 253 254 final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); 255 onCreateForTriState(packageName); 256 mHeaderPreference = findPreference(KEY_PREF_HEADER); 257 258 if (packageName != null) { 259 mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); 260 } 261 } 262 263 @Override onResume()264 public void onResume() { 265 super.onResume(); 266 267 initHeader(); 268 mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); 269 initPreferenceForTriState(getContext()); 270 mExecutor.execute(() -> { 271 String packageName = 272 getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); 273 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider() 274 .action( 275 getContext(), 276 SettingsEnums.OPEN_APP_BATTERY_USAGE, 277 packageName); 278 }); 279 mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); 280 } 281 282 @Override onPause()283 public void onPause() { 284 super.onPause(); 285 286 final int selectedPreference = getSelectedPreference(); 287 288 notifyBackupManager(); 289 mLogStringBuilder.append(", onPause mode = ").append(selectedPreference); 290 logMetricCategory(selectedPreference); 291 292 mExecutor.execute(() -> { 293 BatteryOptimizeLogUtils.writeLog( 294 getContext().getApplicationContext(), 295 Action.LEAVE, 296 BatteryOptimizeLogUtils.getPackageNameWithUserId( 297 mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), 298 mLogStringBuilder.toString()); 299 }); 300 Log.d(TAG, "Leave with mode: " + selectedPreference); 301 } 302 303 @VisibleForTesting notifyBackupManager()304 void notifyBackupManager() { 305 if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { 306 final BackupManager backupManager = mBackupManager != null 307 ? mBackupManager : new BackupManager(getContext()); 308 backupManager.dataChanged(); 309 } 310 } 311 312 @VisibleForTesting initHeader()313 void initHeader() { 314 final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); 315 final Activity context = getActivity(); 316 final Bundle bundle = getArguments(); 317 EntityHeaderController controller = EntityHeaderController 318 .newInstance(context, this, appSnippet) 319 .setRecyclerView(getListView(), getSettingsLifecycle()) 320 .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, 321 EntityHeaderController.ActionType.ACTION_NONE); 322 323 if (mAppEntry == null) { 324 controller.setLabel(bundle.getString(EXTRA_LABEL)); 325 326 final int iconId = bundle.getInt(EXTRA_ICON_ID, 0); 327 if (iconId == 0) { 328 controller.setIcon(context.getPackageManager().getDefaultActivityIcon()); 329 } else { 330 controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID))); 331 } 332 } else { 333 mState.ensureIcon(mAppEntry); 334 controller.setLabel(mAppEntry); 335 controller.setIcon(mAppEntry); 336 controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info)); 337 } 338 339 controller.setSummary(getHeaderSummary(bundle)); 340 controller.done(context, true /* rebindActions */); 341 } 342 343 @VisibleForTesting initPreferenceForTriState(Context context)344 void initPreferenceForTriState(Context context) { 345 final String stateString; 346 final String footerString; 347 348 if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { 349 // Present optimized only string when the package name is invalid. 350 stateString = context.getString(R.string.manager_battery_usage_optimized_only); 351 footerString = context.getString( 352 R.string.manager_battery_usage_footer_limited, stateString); 353 } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { 354 // Present unrestricted only string when the package is system or default active app. 355 stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); 356 footerString = context.getString( 357 R.string.manager_battery_usage_footer_limited, stateString); 358 } else { 359 // Present default string to normal app. 360 footerString = context.getString(R.string.manager_battery_usage_footer); 361 } 362 mFooterPreference.setTitle(footerString); 363 final Intent helpIntent = HelpUtils.getHelpIntent(context, context.getString( 364 R.string.help_url_app_usage_settings), /*backupContext=*/ ""); 365 if (helpIntent != null) { 366 mFooterPreference.setLearnMoreAction(v -> 367 startActivityForResult(helpIntent, /*requestCode=*/ 0)); 368 mFooterPreference.setLearnMoreText( 369 context.getString(R.string.manager_battery_usage_link_a11y)); 370 } 371 } 372 373 @Override getMetricsCategory()374 public int getMetricsCategory() { 375 return SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL; 376 } 377 378 @Override getLogTag()379 protected String getLogTag() { 380 return TAG; 381 } 382 383 @Override getPreferenceScreenResId()384 protected int getPreferenceScreenResId() { 385 return R.xml.power_usage_detail; 386 } 387 388 @Override createPreferenceControllers(Context context)389 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 390 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 391 final Bundle bundle = getArguments(); 392 final int uid = bundle.getInt(EXTRA_UID, 0); 393 final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); 394 395 mAppButtonsPreferenceController = new AppButtonsPreferenceController( 396 (SettingsActivity) getActivity(), this, getSettingsLifecycle(), 397 packageName, mState, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); 398 controllers.add(mAppButtonsPreferenceController); 399 controllers.add(new UnrestrictedPreferenceController(context, uid, packageName)); 400 controllers.add(new OptimizedPreferenceController(context, uid, packageName)); 401 controllers.add(new RestrictedPreferenceController(context, uid, packageName)); 402 403 return controllers; 404 } 405 406 @Override onActivityResult(int requestCode, int resultCode, Intent data)407 public void onActivityResult(int requestCode, int resultCode, Intent data) { 408 super.onActivityResult(requestCode, resultCode, data); 409 if (mAppButtonsPreferenceController != null) { 410 mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); 411 } 412 } 413 414 @Override handleDialogClick(int id)415 public void handleDialogClick(int id) { 416 if (mAppButtonsPreferenceController != null) { 417 mAppButtonsPreferenceController.handleDialogClick(id); 418 } 419 } 420 421 @Override onRadioButtonClicked(SelectorWithWidgetPreference selected)422 public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { 423 final String selectedKey = selected.getKey(); 424 updatePreferenceState(mUnrestrictedPreference, selectedKey); 425 updatePreferenceState(mOptimizePreference, selectedKey); 426 updatePreferenceState(mRestrictedPreference, selectedKey); 427 mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY); 428 } 429 updatePreferenceState(SelectorWithWidgetPreference preference, String selectedKey)430 private void updatePreferenceState(SelectorWithWidgetPreference preference, 431 String selectedKey) { 432 preference.setChecked(selectedKey.equals(preference.getKey())); 433 } 434 logMetricCategory(int selectedKey)435 private void logMetricCategory(int selectedKey) { 436 if (selectedKey == mOptimizationMode) { 437 return; 438 } 439 440 int metricCategory = 0; 441 switch (selectedKey) { 442 case BatteryOptimizeUtils.MODE_UNRESTRICTED: 443 metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED; 444 break; 445 case BatteryOptimizeUtils.MODE_OPTIMIZED: 446 metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED; 447 break; 448 case BatteryOptimizeUtils.MODE_RESTRICTED: 449 metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_RESTRICTED; 450 break; 451 } 452 if (metricCategory == 0) { 453 return; 454 } 455 int finalMetricCategory = metricCategory; 456 mExecutor.execute(() -> { 457 String packageName = 458 getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); 459 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider() 460 .action( 461 /* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE, 462 /* action */ finalMetricCategory, 463 /* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE, 464 packageName, 465 getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); 466 }); 467 } 468 onCreateForTriState(String packageName)469 private void onCreateForTriState(String packageName) { 470 mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); 471 mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); 472 mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED); 473 mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE); 474 mUnrestrictedPreference.setOnClickListener(this); 475 mOptimizePreference.setOnClickListener(this); 476 mRestrictedPreference.setOnClickListener(this); 477 478 mBatteryOptimizeUtils = new BatteryOptimizeUtils( 479 getContext(), getArguments().getInt(EXTRA_UID), packageName); 480 } 481 getSelectedPreference()482 private int getSelectedPreference() { 483 if (mRestrictedPreference.isChecked()) { 484 return BatteryOptimizeUtils.MODE_RESTRICTED; 485 } else if (mUnrestrictedPreference.isChecked()) { 486 return BatteryOptimizeUtils.MODE_UNRESTRICTED; 487 } else if (mOptimizePreference.isChecked()) { 488 return BatteryOptimizeUtils.MODE_OPTIMIZED; 489 } else { 490 return BatteryOptimizeUtils.MODE_UNKNOWN; 491 } 492 } 493 getHeaderSummary(Bundle bundle)494 private CharSequence getHeaderSummary(Bundle bundle) { 495 final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME); 496 final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME); 497 final long screenOnTimeInMs = bundle.getLong(EXTRA_SCREEN_ON_TIME); 498 final String slotTime = bundle.getString(EXTRA_SLOT_TIME, null); 499 final String usageSummary = BatteryUtils.buildBatteryUsageTimeSummary(getContext(), 500 /* isSystem= */ false, foregroundTimeMs, backgroundTimeMs, screenOnTimeInMs); 501 502 if (usageSummary.isEmpty()) { 503 return getText(R.string.battery_usage_without_time); 504 } else { 505 CharSequence slotSummary = slotTime == null 506 ? getText(R.string.battery_usage_since_last_full_charge) : slotTime; 507 return String.format("%s\n(%s)", usageSummary, slotSummary); 508 } 509 } 510 getLoggingPackageName(Context context, String originalPackingName)511 private static String getLoggingPackageName(Context context, String originalPackingName) { 512 return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName) 513 ? originalPackingName : PACKAGE_NAME_NONE; 514 } 515 } 516