• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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