1 /* 2 * Copyright (C) 2019 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 android.accessibilityservice.AccessibilityServiceInfo; 20 import android.app.Activity; 21 import android.app.admin.DevicePolicyManager; 22 import android.app.settings.SettingsEnums; 23 import android.content.ComponentName; 24 import android.content.Intent; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.ServiceInfo; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import android.view.accessibility.AccessibilityManager; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.settings.R; 35 import com.android.settings.core.InstrumentedFragment; 36 import com.android.settings.core.SubSettingLauncher; 37 import com.android.settingslib.accessibility.AccessibilityUtils; 38 39 import java.util.List; 40 import java.util.Set; 41 42 public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment { 43 44 private final static String TAG = "A11yDetailsSettings"; 45 46 @Override getMetricsCategory()47 public int getMetricsCategory() { 48 return SettingsEnums.ACCESSIBILITY_DETAILS_SETTINGS; 49 } 50 51 @Override onCreate(Bundle savedInstanceState)52 public void onCreate(Bundle savedInstanceState) { 53 super.onCreate(savedInstanceState); 54 55 // In case the Intent doesn't have component name, go to a11y services list. 56 final String extraComponentName = getActivity().getIntent().getStringExtra( 57 Intent.EXTRA_COMPONENT_NAME); 58 if (extraComponentName == null) { 59 Log.w(TAG, "Open accessibility services list due to no component name."); 60 openAccessibilitySettingsAndFinish(); 61 return; 62 } 63 64 // In case the A11yServiceInfo doesn't exist, go to ally services list. 65 final ComponentName componentName = ComponentName.unflattenFromString(extraComponentName); 66 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(componentName); 67 if (info == null) { 68 Log.w(TAG, "Open accessibility services list due to invalid component name."); 69 openAccessibilitySettingsAndFinish(); 70 return; 71 } 72 73 // In case this accessibility service isn't permitted, go to a11y services list. 74 if (!isServiceAllowed(componentName.getPackageName())) { 75 Log.w(TAG, 76 "Open accessibility services list due to target accessibility service is " 77 + "prohibited by Device Admin."); 78 openAccessibilitySettingsAndFinish(); 79 return; 80 } 81 82 openAccessibilityDetailsSettingsAndFinish(buildArguments(info)); 83 } 84 85 @VisibleForTesting openAccessibilitySettingsAndFinish()86 void openAccessibilitySettingsAndFinish() { 87 new SubSettingLauncher(getActivity()) 88 .setDestination(AccessibilitySettings.class.getName()) 89 .setSourceMetricsCategory(getMetricsCategory()) 90 .launch(); 91 finish(); 92 } 93 94 @VisibleForTesting openAccessibilityDetailsSettingsAndFinish(Bundle arguments)95 void openAccessibilityDetailsSettingsAndFinish(Bundle arguments) { 96 new SubSettingLauncher(getActivity()) 97 .setDestination(ToggleAccessibilityServicePreferenceFragment.class.getName()) 98 .setSourceMetricsCategory(getMetricsCategory()) 99 .setArguments(arguments) 100 .launch(); 101 finish(); 102 } 103 104 @VisibleForTesting isServiceAllowed(String packageName)105 boolean isServiceAllowed(String packageName) { 106 final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class); 107 final List<String> permittedServices = dpm.getPermittedAccessibilityServices( 108 UserHandle.myUserId()); 109 return (permittedServices == null || permittedServices.contains(packageName)); 110 } 111 getAccessibilityServiceInfo(ComponentName componentName)112 private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) { 113 if (componentName == null) { 114 return null; 115 } 116 117 final List<AccessibilityServiceInfo> serviceInfos = AccessibilityManager.getInstance( 118 getActivity()).getInstalledAccessibilityServiceList(); 119 final int serviceInfoCount = serviceInfos.size(); 120 for (int i = 0; i < serviceInfoCount; i++) { 121 AccessibilityServiceInfo serviceInfo = serviceInfos.get(i); 122 ResolveInfo resolveInfo = serviceInfo.getResolveInfo(); 123 if (componentName.getPackageName().equals(resolveInfo.serviceInfo.packageName) 124 && componentName.getClassName().equals(resolveInfo.serviceInfo.name)) { 125 return serviceInfo; 126 } 127 } 128 return null; 129 } 130 buildArguments(AccessibilityServiceInfo info)131 private Bundle buildArguments(AccessibilityServiceInfo info) { 132 final ResolveInfo resolveInfo = info.getResolveInfo(); 133 final String title = resolveInfo.loadLabel(getActivity().getPackageManager()).toString(); 134 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 135 final String packageName = serviceInfo.packageName; 136 final ComponentName componentName = new ComponentName(packageName, serviceInfo.name); 137 138 final List<AccessibilityServiceInfo> enabledServiceInfos = AccessibilityManager.getInstance( 139 getActivity()).getEnabledAccessibilityServiceList( 140 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 141 final Set<ComponentName> enabledServices = 142 AccessibilityUtils.getEnabledServicesFromSettings(getActivity()); 143 final boolean serviceEnabled = enabledServices.contains(componentName); 144 String description = info.loadDescription(getActivity().getPackageManager()); 145 if (TextUtils.isEmpty(description)) { 146 description = getString(R.string.accessibility_service_default_description); 147 } 148 149 if (serviceEnabled && AccessibilityUtils.hasServiceCrashed( 150 packageName, serviceInfo.name, enabledServiceInfos)) { 151 // Update the summaries for services that have crashed. 152 description = getString(R.string.accessibility_description_state_stopped); 153 } 154 155 final Bundle extras = new Bundle(); 156 extras.putString(AccessibilitySettings.EXTRA_PREFERENCE_KEY, 157 componentName.flattenToString()); 158 extras.putBoolean(AccessibilitySettings.EXTRA_CHECKED, serviceEnabled); 159 extras.putString(AccessibilitySettings.EXTRA_TITLE, title); 160 extras.putParcelable(AccessibilitySettings.EXTRA_RESOLVE_INFO, resolveInfo); 161 extras.putString(AccessibilitySettings.EXTRA_SUMMARY, description); 162 163 final String settingsClassName = info.getSettingsActivityName(); 164 if (!TextUtils.isEmpty(settingsClassName)) { 165 extras.putString(AccessibilitySettings.EXTRA_SETTINGS_TITLE, 166 getString(R.string.accessibility_menu_item_settings)); 167 extras.putString(AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME, 168 new ComponentName(packageName, settingsClassName).flattenToString()); 169 } 170 extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName); 171 172 return extras; 173 } 174 finish()175 private void finish() { 176 Activity activity = getActivity(); 177 if (activity == null) { 178 return; 179 } 180 activity.finish(); 181 } 182 } 183