1 /* 2 * Copyright (C) 2014 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.search; 18 19 import android.accessibilityservice.AccessibilityService; 20 import android.accessibilityservice.AccessibilityServiceInfo; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.content.pm.ServiceInfo; 26 import android.database.ContentObserver; 27 import android.hardware.input.InputManager; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.UserHandle; 33 import android.print.PrintManager; 34 import android.printservice.PrintService; 35 import android.printservice.PrintServiceInfo; 36 import android.provider.UserDictionary; 37 import android.view.accessibility.AccessibilityManager; 38 import android.view.inputmethod.InputMethodInfo; 39 import android.view.inputmethod.InputMethodManager; 40 import com.android.internal.content.PackageMonitor; 41 import com.android.settings.accessibility.AccessibilitySettings; 42 import com.android.settings.inputmethod.InputMethodAndLanguageSettings; 43 import com.android.settings.print.PrintSettingsFragment; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 public final class DynamicIndexableContentMonitor extends PackageMonitor implements 49 InputManager.InputDeviceListener { 50 51 private static final long DELAY_PROCESS_PACKAGE_CHANGE = 2000; 52 53 private static final int MSG_PACKAGE_AVAILABLE = 1; 54 private static final int MSG_PACKAGE_UNAVAILABLE = 2; 55 56 private final List<String> mAccessibilityServices = new ArrayList<String>(); 57 private final List<String> mPrintServices = new ArrayList<String>(); 58 private final List<String> mImeServices = new ArrayList<String>(); 59 60 private final Handler mHandler = new Handler() { 61 @Override 62 public void handleMessage(Message msg) { 63 switch (msg.what) { 64 case MSG_PACKAGE_AVAILABLE: { 65 String packageName = (String) msg.obj; 66 handlePackageAvailable(packageName); 67 } break; 68 69 case MSG_PACKAGE_UNAVAILABLE: { 70 String packageName = (String) msg.obj; 71 handlePackageUnavailable(packageName); 72 } break; 73 } 74 } 75 }; 76 77 private final ContentObserver mUserDictionaryContentObserver = 78 new UserDictionaryContentObserver(mHandler); 79 80 private Context mContext; 81 private boolean mHasFeaturePrinting; 82 private boolean mHasFeatureIme; 83 84 private static Intent getAccessibilityServiceIntent(String packageName) { 85 final Intent intent = new Intent(AccessibilityService.SERVICE_INTERFACE); 86 intent.setPackage(packageName); 87 return intent; 88 } 89 90 private static Intent getPrintServiceIntent(String packageName) { 91 final Intent intent = new Intent(PrintService.SERVICE_INTERFACE); 92 intent.setPackage(packageName); 93 return intent; 94 } 95 96 private static Intent getIMEServiceIntent(String packageName) { 97 final Intent intent = new Intent("android.view.InputMethod"); 98 intent.setPackage(packageName); 99 return intent; 100 } 101 102 public void register(Context context) { 103 mContext = context; 104 105 mHasFeaturePrinting = mContext.getPackageManager().hasSystemFeature( 106 PackageManager.FEATURE_PRINTING); 107 mHasFeatureIme = mContext.getPackageManager().hasSystemFeature( 108 PackageManager.FEATURE_INPUT_METHODS); 109 110 // Cache accessibility service packages to know when they go away. 111 AccessibilityManager accessibilityManager = (AccessibilityManager) 112 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); 113 List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager 114 .getInstalledAccessibilityServiceList(); 115 final int accessibilityServiceCount = accessibilityServices.size(); 116 for (int i = 0; i < accessibilityServiceCount; i++) { 117 AccessibilityServiceInfo accessibilityService = accessibilityServices.get(i); 118 ResolveInfo resolveInfo = accessibilityService.getResolveInfo(); 119 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 120 continue; 121 } 122 mAccessibilityServices.add(resolveInfo.serviceInfo.packageName); 123 } 124 125 if (mHasFeaturePrinting) { 126 // Cache print service packages to know when they go away. 127 PrintManager printManager = (PrintManager) 128 mContext.getSystemService(Context.PRINT_SERVICE); 129 List<PrintServiceInfo> printServices = printManager.getInstalledPrintServices(); 130 final int serviceCount = printServices.size(); 131 for (int i = 0; i < serviceCount; i++) { 132 PrintServiceInfo printService = printServices.get(i); 133 ResolveInfo resolveInfo = printService.getResolveInfo(); 134 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 135 continue; 136 } 137 mPrintServices.add(resolveInfo.serviceInfo.packageName); 138 } 139 } 140 141 // Cache IME service packages to know when they go away. 142 if (mHasFeatureIme) { 143 InputMethodManager imeManager = (InputMethodManager) 144 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); 145 List<InputMethodInfo> inputMethods = imeManager.getInputMethodList(); 146 final int inputMethodCount = inputMethods.size(); 147 for (int i = 0; i < inputMethodCount; i++) { 148 InputMethodInfo inputMethod = inputMethods.get(i); 149 ServiceInfo serviceInfo = inputMethod.getServiceInfo(); 150 if (serviceInfo == null) continue; 151 mImeServices.add(serviceInfo.packageName); 152 } 153 154 // Watch for related content URIs. 155 mContext.getContentResolver().registerContentObserver( 156 UserDictionary.Words.CONTENT_URI, true, mUserDictionaryContentObserver); 157 } 158 159 // Watch for input device changes. 160 InputManager inputManager = (InputManager) context.getSystemService( 161 Context.INPUT_SERVICE); 162 inputManager.registerInputDeviceListener(this, mHandler); 163 164 // Start tracking packages. 165 register(context, Looper.getMainLooper(), UserHandle.CURRENT, false); 166 } 167 168 public void unregister() { 169 super.unregister(); 170 171 InputManager inputManager = (InputManager) mContext.getSystemService( 172 Context.INPUT_SERVICE); 173 inputManager.unregisterInputDeviceListener(this); 174 175 if (mHasFeatureIme) { 176 mContext.getContentResolver().unregisterContentObserver( 177 mUserDictionaryContentObserver); 178 } 179 180 mAccessibilityServices.clear(); 181 mPrintServices.clear(); 182 mImeServices.clear(); 183 } 184 185 // Covers installed, appeared external storage with the package, upgraded. 186 @Override 187 public void onPackageAppeared(String packageName, int uid) { 188 postMessage(MSG_PACKAGE_AVAILABLE, packageName); 189 } 190 191 // Covers uninstalled, removed external storage with the package. 192 @Override 193 public void onPackageDisappeared(String packageName, int uid) { 194 postMessage(MSG_PACKAGE_UNAVAILABLE, packageName); 195 } 196 197 // Covers enabled, disabled. 198 @Override 199 public void onPackageModified(String packageName) { 200 super.onPackageModified(packageName); 201 final int state = mContext.getPackageManager().getApplicationEnabledSetting( 202 packageName); 203 if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT 204 || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 205 postMessage(MSG_PACKAGE_AVAILABLE, packageName); 206 } else { 207 postMessage(MSG_PACKAGE_UNAVAILABLE, packageName); 208 } 209 } 210 211 @Override 212 public void onInputDeviceAdded(int deviceId) { 213 Index.getInstance(mContext).updateFromClassNameResource( 214 InputMethodAndLanguageSettings.class.getName(), false, true); 215 } 216 217 @Override 218 public void onInputDeviceRemoved(int deviceId) { 219 onInputDeviceChanged(deviceId); 220 } 221 222 @Override 223 public void onInputDeviceChanged(int deviceId) { 224 Index.getInstance(mContext).updateFromClassNameResource( 225 InputMethodAndLanguageSettings.class.getName(), true, true); 226 } 227 228 private void postMessage(int what, String packageName) { 229 Message message = mHandler.obtainMessage(what, packageName); 230 mHandler.sendMessageDelayed(message, DELAY_PROCESS_PACKAGE_CHANGE); 231 } 232 233 private void handlePackageAvailable(String packageName) { 234 if (!mAccessibilityServices.contains(packageName)) { 235 final Intent intent = getAccessibilityServiceIntent(packageName); 236 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) { 237 mAccessibilityServices.add(packageName); 238 Index.getInstance(mContext).updateFromClassNameResource( 239 AccessibilitySettings.class.getName(), false, true); 240 } 241 } 242 243 if (mHasFeaturePrinting) { 244 if (!mPrintServices.contains(packageName)) { 245 final Intent intent = getPrintServiceIntent(packageName); 246 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) { 247 mPrintServices.add(packageName); 248 Index.getInstance(mContext).updateFromClassNameResource( 249 PrintSettingsFragment.class.getName(), false, true); 250 } 251 } 252 } 253 254 if (mHasFeatureIme) { 255 if (!mImeServices.contains(packageName)) { 256 Intent intent = getIMEServiceIntent(packageName); 257 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) { 258 mImeServices.add(packageName); 259 Index.getInstance(mContext).updateFromClassNameResource( 260 InputMethodAndLanguageSettings.class.getName(), false, true); 261 } 262 } 263 } 264 } 265 266 private void handlePackageUnavailable(String packageName) { 267 final int accessibilityIndex = mAccessibilityServices.indexOf(packageName); 268 if (accessibilityIndex >= 0) { 269 mAccessibilityServices.remove(accessibilityIndex); 270 Index.getInstance(mContext).updateFromClassNameResource( 271 AccessibilitySettings.class.getName(), true, true); 272 } 273 274 if (mHasFeaturePrinting) { 275 final int printIndex = mPrintServices.indexOf(packageName); 276 if (printIndex >= 0) { 277 mPrintServices.remove(printIndex); 278 Index.getInstance(mContext).updateFromClassNameResource( 279 PrintSettingsFragment.class.getName(), true, true); 280 } 281 } 282 283 if (mHasFeatureIme) { 284 final int imeIndex = mImeServices.indexOf(packageName); 285 if (imeIndex >= 0) { 286 mImeServices.remove(imeIndex); 287 Index.getInstance(mContext).updateFromClassNameResource( 288 InputMethodAndLanguageSettings.class.getName(), true, true); 289 } 290 } 291 } 292 293 private final class UserDictionaryContentObserver extends ContentObserver { 294 295 public UserDictionaryContentObserver(Handler handler) { 296 super(handler); 297 } 298 299 @Override 300 public void onChange(boolean selfChange, Uri uri) { 301 if (UserDictionary.Words.CONTENT_URI.equals(uri)) { 302 Index.getInstance(mContext).updateFromClassNameResource( 303 InputMethodAndLanguageSettings.class.getName(), true, true); 304 } 305 }; 306 } 307 } 308