Home | History | Annotate | Download | only in search
      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