1 /* 2 * Copyright (C) 2013 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.accessibility; 18 19 import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; 20 import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled; 21 import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.app.Dialog; 25 import android.app.settings.SettingsEnums; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.DialogInterface; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.ResolveInfo; 35 import android.content.pm.ServiceInfo; 36 import android.net.Uri; 37 import android.os.Bundle; 38 import android.os.SystemClock; 39 import android.text.TextUtils; 40 import android.util.Log; 41 import android.view.Menu; 42 import android.view.MenuInflater; 43 import android.view.View; 44 import android.view.accessibility.AccessibilityManager; 45 import android.widget.Switch; 46 47 import androidx.annotation.Nullable; 48 49 import com.android.settings.R; 50 import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType; 51 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; 52 import com.android.settings.overlay.FeatureFactory; 53 import com.android.settingslib.accessibility.AccessibilityUtils; 54 55 import java.util.List; 56 import java.util.concurrent.atomic.AtomicBoolean; 57 58 /** Fragment for providing toggle bar and basic accessibility service setup. */ 59 public class ToggleAccessibilityServicePreferenceFragment extends 60 ToggleFeaturePreferenceFragment { 61 62 private static final String TAG = "ToggleAccessibilityServicePreferenceFragment"; 63 private static final String KEY_HAS_LOGGED = "has_logged"; 64 private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false); 65 66 private static final String EMPTY_STRING = ""; 67 68 private Dialog mWarningDialog; 69 private ComponentName mTileComponentName; 70 private BroadcastReceiver mPackageRemovedReceiver; 71 private boolean mDisabledStateLogged = false; 72 private long mStartTimeMillsForLogging = 0; 73 74 @Override getMetricsCategory()75 public int getMetricsCategory() { 76 // Retrieve from getArguments() directly because this function will be executed from 77 // onAttach(), but variable mComponentName only available after onProcessArguments() 78 // which comes from onCreateView(). 79 final ComponentName componentName = getArguments().getParcelable( 80 AccessibilitySettings.EXTRA_COMPONENT_NAME); 81 82 return FeatureFactory.getFactory(getActivity().getApplicationContext()) 83 .getAccessibilityMetricsFeatureProvider() 84 .getDownloadedFeatureMetricsCategory(componentName); 85 } 86 87 @Override onCreateOptionsMenu(Menu menu, MenuInflater infalter)88 public void onCreateOptionsMenu(Menu menu, MenuInflater infalter) { 89 // Do not call super. We don't want to see the "Help & feedback" option on this page so as 90 // not to confuse users who think they might be able to send feedback about a specific 91 // accessibility service from this page. 92 } 93 94 @Override onCreate(Bundle savedInstanceState)95 public void onCreate(Bundle savedInstanceState) { 96 super.onCreate(savedInstanceState); 97 if (savedInstanceState != null) { 98 if (savedInstanceState.containsKey(KEY_HAS_LOGGED)) { 99 mDisabledStateLogged = savedInstanceState.getBoolean(KEY_HAS_LOGGED); 100 } 101 } 102 } 103 104 @Override registerKeysToObserverCallback( AccessibilitySettingsContentObserver contentObserver)105 protected void registerKeysToObserverCallback( 106 AccessibilitySettingsContentObserver contentObserver) { 107 super.registerKeysToObserverCallback(contentObserver); 108 contentObserver.registerObserverCallback(key -> updateSwitchBarToggleSwitch()); 109 } 110 111 @Override onStart()112 public void onStart() { 113 super.onStart(); 114 final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo(); 115 if (serviceInfo == null) { 116 getActivity().finishAndRemoveTask(); 117 } else if (!AccessibilityUtil.isSystemApp(serviceInfo)) { 118 registerPackageRemoveReceiver(); 119 } 120 } 121 122 @Override onResume()123 public void onResume() { 124 super.onResume(); 125 updateSwitchBarToggleSwitch(); 126 } 127 128 @Override onPause()129 public void onPause() { 130 super.onPause(); 131 } 132 133 @Override onSaveInstanceState(Bundle outState)134 public void onSaveInstanceState(Bundle outState) { 135 if (mStartTimeMillsForLogging > 0) { 136 outState.putBoolean(KEY_HAS_LOGGED, mDisabledStateLogged); 137 } 138 super.onSaveInstanceState(outState); 139 } 140 141 @Override onPreferenceToggled(String preferenceKey, boolean enabled)142 public void onPreferenceToggled(String preferenceKey, boolean enabled) { 143 ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey); 144 logAccessibilityServiceEnabled(toggledService, enabled); 145 if (!enabled) { 146 logDisabledState(toggledService.getPackageName()); 147 } 148 AccessibilityUtils.setAccessibilityServiceState(getPrefContext(), toggledService, enabled); 149 } 150 151 // IMPORTANT: Refresh the info since there are dynamically changing 152 // capabilities. For 153 // example, before JellyBean MR2 the user was granting the explore by touch 154 // one. 155 @Nullable getAccessibilityServiceInfo()156 AccessibilityServiceInfo getAccessibilityServiceInfo() { 157 final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance( 158 getPrefContext()).getInstalledAccessibilityServiceList(); 159 160 for (int i = 0, count = infos.size(); i < count; i++) { 161 AccessibilityServiceInfo serviceInfo = infos.get(i); 162 ResolveInfo resolveInfo = serviceInfo.getResolveInfo(); 163 if (mComponentName.getPackageName().equals(resolveInfo.serviceInfo.packageName) 164 && mComponentName.getClassName().equals(resolveInfo.serviceInfo.name)) { 165 return serviceInfo; 166 } 167 } 168 return null; 169 } 170 171 @Override onCreateDialog(int dialogId)172 public Dialog onCreateDialog(int dialogId) { 173 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 174 switch (dialogId) { 175 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: 176 if (info == null) { 177 return null; 178 } 179 mWarningDialog = AccessibilityServiceWarning 180 .createCapabilitiesDialog(getPrefContext(), info, 181 this::onDialogButtonFromEnableToggleClicked, 182 this::onDialogButtonFromUninstallClicked); 183 return mWarningDialog; 184 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: 185 if (info == null) { 186 return null; 187 } 188 mWarningDialog = AccessibilityServiceWarning 189 .createCapabilitiesDialog(getPrefContext(), info, 190 this::onDialogButtonFromShortcutToggleClicked, 191 this::onDialogButtonFromUninstallClicked); 192 return mWarningDialog; 193 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: 194 if (info == null) { 195 return null; 196 } 197 mWarningDialog = AccessibilityServiceWarning 198 .createCapabilitiesDialog(getPrefContext(), info, 199 this::onDialogButtonFromShortcutClicked, 200 this::onDialogButtonFromUninstallClicked); 201 return mWarningDialog; 202 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: 203 if (info == null) { 204 return null; 205 } 206 mWarningDialog = AccessibilityServiceWarning 207 .createDisableDialog(getPrefContext(), info, 208 this::onDialogButtonFromDisableToggleClicked); 209 return mWarningDialog; 210 default: 211 return super.onCreateDialog(dialogId); 212 } 213 } 214 215 @Override getDialogMetricsCategory(int dialogId)216 public int getDialogMetricsCategory(int dialogId) { 217 switch (dialogId) { 218 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: 219 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: 220 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: 221 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_ENABLE; 222 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: 223 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_DISABLE; 224 case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: 225 return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL; 226 default: 227 return super.getDialogMetricsCategory(dialogId); 228 } 229 } 230 231 @Override getUserShortcutTypes()232 int getUserShortcutTypes() { 233 return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), 234 mComponentName); 235 } 236 237 @Override getTileComponentName()238 ComponentName getTileComponentName() { 239 return mTileComponentName; 240 } 241 242 @Override getTileTooltipContent(@uickSettingsTooltipType int type)243 CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) { 244 final ComponentName componentName = getTileComponentName(); 245 if (componentName == null) { 246 return null; 247 } 248 249 final CharSequence tileName = loadTileLabel(getPrefContext(), componentName); 250 if (tileName == null) { 251 return null; 252 } 253 254 final int titleResId = type == QuickSettingsTooltipType.GUIDE_TO_EDIT 255 ? R.string.accessibility_service_qs_tooltip_content 256 : R.string.accessibility_service_auto_added_qs_tooltip_content; 257 return getString(titleResId, tileName); 258 } 259 260 @Override updateSwitchBarToggleSwitch()261 protected void updateSwitchBarToggleSwitch() { 262 final boolean checked = isAccessibilityServiceEnabled(); 263 if (mToggleServiceSwitchPreference.isChecked() == checked) { 264 return; 265 } 266 mToggleServiceSwitchPreference.setChecked(checked); 267 } 268 isAccessibilityServiceEnabled()269 private boolean isAccessibilityServiceEnabled() { 270 return AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext()) 271 .contains(mComponentName); 272 } 273 274 @Override onActivityResult(int requestCode, int resultCode, Intent data)275 public void onActivityResult(int requestCode, int resultCode, Intent data) { 276 } 277 registerPackageRemoveReceiver()278 private void registerPackageRemoveReceiver() { 279 if (mPackageRemovedReceiver != null || getContext() == null) { 280 return; 281 } 282 mPackageRemovedReceiver = new BroadcastReceiver() { 283 @Override 284 public void onReceive(Context context, Intent intent) { 285 final String packageName = intent.getData().getSchemeSpecificPart(); 286 if (TextUtils.equals(mComponentName.getPackageName(), packageName)) { 287 getActivity().finishAndRemoveTask(); 288 } 289 } 290 }; 291 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 292 filter.addDataScheme("package"); 293 getContext().registerReceiver(mPackageRemovedReceiver, filter); 294 } 295 unregisterPackageRemoveReceiver()296 private void unregisterPackageRemoveReceiver() { 297 if (mPackageRemovedReceiver == null || getContext() == null) { 298 return; 299 } 300 getContext().unregisterReceiver(mPackageRemovedReceiver); 301 mPackageRemovedReceiver = null; 302 } 303 isServiceSupportAccessibilityButton()304 private boolean isServiceSupportAccessibilityButton() { 305 final AccessibilityManager ams = getPrefContext().getSystemService( 306 AccessibilityManager.class); 307 final List<AccessibilityServiceInfo> services = ams.getInstalledAccessibilityServiceList(); 308 309 for (AccessibilityServiceInfo info : services) { 310 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 311 ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; 312 if (serviceInfo != null && TextUtils.equals(serviceInfo.name, 313 getAccessibilityServiceInfo().getResolveInfo().serviceInfo.name)) { 314 return true; 315 } 316 } 317 } 318 319 return false; 320 } 321 handleConfirmServiceEnabled(boolean confirmed)322 private void handleConfirmServiceEnabled(boolean confirmed) { 323 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed); 324 onPreferenceToggled(mPreferenceKey, confirmed); 325 } 326 327 @Override onSwitchChanged(Switch switchView, boolean isChecked)328 public void onSwitchChanged(Switch switchView, boolean isChecked) { 329 if (isChecked != isAccessibilityServiceEnabled()) { 330 onPreferenceClick(isChecked); 331 } 332 } 333 334 @Override onToggleClicked(ShortcutPreference preference)335 public void onToggleClicked(ShortcutPreference preference) { 336 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 337 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 338 if (preference.isChecked()) { 339 if (!mToggleServiceSwitchPreference.isChecked()) { 340 preference.setChecked(false); 341 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE); 342 } else { 343 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, 344 mComponentName); 345 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 346 } 347 } else { 348 AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, 349 mComponentName); 350 } 351 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 352 } 353 354 @Override onSettingsClicked(ShortcutPreference preference)355 public void onSettingsClicked(ShortcutPreference preference) { 356 final boolean isServiceOnOrShortcutAdded = mShortcutPreference.isChecked() 357 || mToggleServiceSwitchPreference.isChecked(); 358 showPopupDialog(isServiceOnOrShortcutAdded ? DialogEnums.EDIT_SHORTCUT 359 : DialogEnums.ENABLE_WARNING_FROM_SHORTCUT); 360 } 361 362 @Override onProcessArguments(Bundle arguments)363 protected void onProcessArguments(Bundle arguments) { 364 super.onProcessArguments(arguments); 365 // Settings title and intent. 366 String settingsTitle = arguments.getString(AccessibilitySettings.EXTRA_SETTINGS_TITLE); 367 String settingsComponentName = arguments.getString( 368 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME); 369 if (!TextUtils.isEmpty(settingsTitle) && !TextUtils.isEmpty(settingsComponentName)) { 370 Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent( 371 ComponentName.unflattenFromString(settingsComponentName.toString())); 372 if (!getPackageManager().queryIntentActivities(settingsIntent, 0).isEmpty()) { 373 mSettingsTitle = settingsTitle; 374 mSettingsIntent = settingsIntent; 375 setHasOptionsMenu(true); 376 } 377 } 378 379 mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME); 380 381 // Settings animated image. 382 final int animatedImageRes = arguments.getInt( 383 AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES); 384 if (animatedImageRes > 0) { 385 mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 386 .authority(mComponentName.getPackageName()) 387 .appendPath(String.valueOf(animatedImageRes)) 388 .build(); 389 } 390 391 // Get Accessibility service name. 392 mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel( 393 getPackageManager()); 394 395 if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) { 396 final String tileServiceComponentName = arguments.getString( 397 AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME); 398 mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName); 399 } 400 401 mStartTimeMillsForLogging = arguments.getLong(AccessibilitySettings.EXTRA_TIME_FOR_LOGGING); 402 } 403 onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which)404 private void onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which) { 405 switch (which) { 406 case DialogInterface.BUTTON_POSITIVE: 407 handleConfirmServiceEnabled(/* confirmed= */ false); 408 break; 409 case DialogInterface.BUTTON_NEGATIVE: 410 handleConfirmServiceEnabled(/* confirmed= */ true); 411 break; 412 default: 413 throw new IllegalArgumentException("Unexpected button identifier"); 414 } 415 } 416 onDialogButtonFromEnableToggleClicked(View view)417 private void onDialogButtonFromEnableToggleClicked(View view) { 418 final int viewId = view.getId(); 419 if (viewId == R.id.permission_enable_allow_button) { 420 onAllowButtonFromEnableToggleClicked(); 421 } else if (viewId == R.id.permission_enable_deny_button) { 422 onDenyButtonFromEnableToggleClicked(); 423 } else { 424 throw new IllegalArgumentException("Unexpected view id"); 425 } 426 } 427 onDialogButtonFromUninstallClicked()428 private void onDialogButtonFromUninstallClicked() { 429 mWarningDialog.dismiss(); 430 final Intent uninstallIntent = createUninstallPackageActivityIntent(); 431 if (uninstallIntent == null) { 432 return; 433 } 434 startActivity(uninstallIntent); 435 } 436 437 @Nullable createUninstallPackageActivityIntent()438 private Intent createUninstallPackageActivityIntent() { 439 final AccessibilityServiceInfo a11yServiceInfo = getAccessibilityServiceInfo(); 440 if (a11yServiceInfo == null) { 441 Log.w(TAG, "createUnInstallIntent -- invalid a11yServiceInfo"); 442 return null; 443 } 444 final ApplicationInfo appInfo = 445 a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo; 446 final Uri packageUri = Uri.parse("package:" + appInfo.packageName); 447 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 448 return uninstallIntent; 449 } 450 451 @Override onStop()452 public void onStop() { 453 super.onStop(); 454 unregisterPackageRemoveReceiver(); 455 } 456 onAllowButtonFromEnableToggleClicked()457 private void onAllowButtonFromEnableToggleClicked() { 458 handleConfirmServiceEnabled(/* confirmed= */ true); 459 if (isServiceSupportAccessibilityButton()) { 460 mIsDialogShown.set(false); 461 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 462 } 463 mWarningDialog.dismiss(); 464 } 465 onDenyButtonFromEnableToggleClicked()466 private void onDenyButtonFromEnableToggleClicked() { 467 handleConfirmServiceEnabled(/* confirmed= */ false); 468 mWarningDialog.dismiss(); 469 } 470 onDialogButtonFromShortcutToggleClicked(View view)471 void onDialogButtonFromShortcutToggleClicked(View view) { 472 final int viewId = view.getId(); 473 if (viewId == R.id.permission_enable_allow_button) { 474 onAllowButtonFromShortcutToggleClicked(); 475 } else if (viewId == R.id.permission_enable_deny_button) { 476 onDenyButtonFromShortcutToggleClicked(); 477 } else { 478 throw new IllegalArgumentException("Unexpected view id"); 479 } 480 } 481 onAllowButtonFromShortcutToggleClicked()482 private void onAllowButtonFromShortcutToggleClicked() { 483 mShortcutPreference.setChecked(true); 484 485 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 486 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 487 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); 488 489 mIsDialogShown.set(false); 490 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 491 492 mWarningDialog.dismiss(); 493 494 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 495 } 496 onDenyButtonFromShortcutToggleClicked()497 private void onDenyButtonFromShortcutToggleClicked() { 498 mShortcutPreference.setChecked(false); 499 500 mWarningDialog.dismiss(); 501 } 502 onDialogButtonFromShortcutClicked(View view)503 void onDialogButtonFromShortcutClicked(View view) { 504 final int viewId = view.getId(); 505 if (viewId == R.id.permission_enable_allow_button) { 506 onAllowButtonFromShortcutClicked(); 507 } else if (viewId == R.id.permission_enable_deny_button) { 508 onDenyButtonFromShortcutClicked(); 509 } else { 510 throw new IllegalArgumentException("Unexpected view id"); 511 } 512 } 513 onAllowButtonFromShortcutClicked()514 private void onAllowButtonFromShortcutClicked() { 515 mIsDialogShown.set(false); 516 showPopupDialog(DialogEnums.EDIT_SHORTCUT); 517 518 mWarningDialog.dismiss(); 519 } 520 onDenyButtonFromShortcutClicked()521 private void onDenyButtonFromShortcutClicked() { 522 mWarningDialog.dismiss(); 523 } 524 onPreferenceClick(boolean isChecked)525 private boolean onPreferenceClick(boolean isChecked) { 526 if (isChecked) { 527 mToggleServiceSwitchPreference.setChecked(false); 528 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 529 /* disableService */ false); 530 if (!mShortcutPreference.isChecked()) { 531 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_TOGGLE); 532 } else { 533 handleConfirmServiceEnabled(/* confirmed= */ true); 534 if (isServiceSupportAccessibilityButton()) { 535 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 536 } 537 } 538 } else { 539 mToggleServiceSwitchPreference.setChecked(true); 540 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 541 /* enableService */ true); 542 showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE); 543 } 544 return true; 545 } 546 showPopupDialog(int dialogId)547 private void showPopupDialog(int dialogId) { 548 if (mIsDialogShown.compareAndSet(/* expect= */ false, /* update= */ true)) { 549 showDialog(dialogId); 550 setOnDismissListener( 551 dialog -> mIsDialogShown.compareAndSet(/* expect= */ true, /* update= */ 552 false)); 553 } 554 } 555 logDisabledState(String packageName)556 private void logDisabledState(String packageName) { 557 if (mStartTimeMillsForLogging > 0 && !mDisabledStateLogged) { 558 AccessibilityStatsLogUtils.logDisableNonA11yCategoryService( 559 packageName, 560 SystemClock.elapsedRealtime() - mStartTimeMillsForLogging); 561 mDisabledStateLogged = true; 562 } 563 } 564 } 565