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