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