19f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/*
2f04335f899f2cce69f843692a3cb9cec229683c2tturney * Copyright (C) 2017 The Android Open Source Project
39f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
4f04335f899f2cce69f843692a3cb9cec229683c2tturney * Licensed under the Apache License, Version 2.0 (the "License");
5f04335f899f2cce69f843692a3cb9cec229683c2tturney * you may not use this file except in compliance with the License.
6f04335f899f2cce69f843692a3cb9cec229683c2tturney * You may obtain a copy of the License at
79f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
8f04335f899f2cce69f843692a3cb9cec229683c2tturney *      http://www.apache.org/licenses/LICENSE-2.0
99f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Unless required by applicable law or agreed to in writing, software
11f04335f899f2cce69f843692a3cb9cec229683c2tturney * distributed under the License is distributed on an "AS IS" BASIS,
12f04335f899f2cce69f843692a3cb9cec229683c2tturney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f04335f899f2cce69f843692a3cb9cec229683c2tturney * See the License for the specific language governing permissions and
14f04335f899f2cce69f843692a3cb9cec229683c2tturney * limitations under the License.
159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */
169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipackage com.googlecode.android_scripting.interpreter;
189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.BroadcastReceiver;
209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.ContentResolver;
219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Context;
229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Intent;
239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.IntentFilter;
249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.pm.PackageInfo;
259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.pm.PackageManager;
269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.pm.ProviderInfo;
279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.pm.ResolveInfo;
289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.pm.PackageManager.NameNotFoundException;
299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.database.Cursor;
309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.net.Uri;
319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.Log;
339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.SingleThreadExecutor;
349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.interpreter.shell.ShellInterpreter;
359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.ArrayList;
379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.HashMap;
389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.LinkedHashMap;
399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.List;
409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.Map;
419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.Set;
429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.concurrent.CopyOnWriteArraySet;
439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.concurrent.ExecutorService;
449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/**
469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Manages and provides access to the set of available interpreters.
479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */
499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipublic class InterpreterConfiguration {
509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final InterpreterListener mListener;
529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final Set<Interpreter> mInterpreterSet;
539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final Set<ConfigurationObserver> mObserverSet;
549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final Context mContext;
559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private volatile boolean mIsDiscoveryComplete = false;
569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public interface ConfigurationObserver {
589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    public void onConfigurationChanged();
599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private class InterpreterListener extends BroadcastReceiver {
629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private final PackageManager mmPackageManager;
639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private final ContentResolver mmResolver;
649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private final ExecutorService mmExecutor;
659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private final Map<String, Interpreter> mmDiscoveredInterpreters;
669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private InterpreterListener(Context context) {
689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmPackageManager = context.getPackageManager();
699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmResolver = context.getContentResolver();
709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmExecutor = new SingleThreadExecutor();
719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmDiscoveredInterpreters = new HashMap<String, Interpreter>();
729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private void discoverForType(final String mime) {
759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmExecutor.execute(new Runnable() {
769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        @Override
779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        public void run() {
789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Intent intent = new Intent(InterpreterConstants.ACTION_DISCOVER_INTERPRETERS);
799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          intent.addCategory(Intent.CATEGORY_LAUNCHER);
809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          intent.setType(mime);
819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          List<ResolveInfo> resolveInfos = mmPackageManager.queryIntentActivities(intent, 0);
829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          for (ResolveInfo info : resolveInfos) {
839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            addInterpreter(info.activityInfo.packageName);
849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          mIsDiscoveryComplete = true;
869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          notifyConfigurationObservers();
879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      });
899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private void discoverAll() {
929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmExecutor.execute(new Runnable() {
939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        @Override
949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        public void run() {
959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Intent intent = new Intent(InterpreterConstants.ACTION_DISCOVER_INTERPRETERS);
969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          intent.addCategory(Intent.CATEGORY_LAUNCHER);
979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          intent.setType(InterpreterConstants.MIME + "*");
989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          List<ResolveInfo> resolveInfos = mmPackageManager.queryIntentActivities(intent, 0);
999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          for (ResolveInfo info : resolveInfos) {
1009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            addInterpreter(info.activityInfo.packageName);
1019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
1029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          mIsDiscoveryComplete = true;
1039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          notifyConfigurationObservers();
1049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
1059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      });
1069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private void notifyConfigurationObservers() {
1099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      for (ConfigurationObserver observer : mObserverSet) {
1109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        observer.onConfigurationChanged();
1119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private void addInterpreter(final String packageName) {
1159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (mmDiscoveredInterpreters.containsKey(packageName)) {
1169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return;
1179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Interpreter discoveredInterpreter = buildInterpreter(packageName);
1199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (discoveredInterpreter == null) {
1209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return;
1219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmDiscoveredInterpreters.put(packageName, discoveredInterpreter);
1239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mInterpreterSet.add(discoveredInterpreter);
1249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Log.v("Interpreter discovered: " + packageName + "\nBinary: "
1259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          + discoveredInterpreter.getBinary());
1269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private void remove(final String packageName) {
1299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (!mmDiscoveredInterpreters.containsKey(packageName)) {
1309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return;
1319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mmExecutor.execute(new Runnable() {
1339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        @Override
1349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        public void run() {
1359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Interpreter interpreter = mmDiscoveredInterpreters.get(packageName);
1369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (interpreter == null) {
1379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            Log.v("Interpreter for " + packageName + " not installed.");
1389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            return;
1399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
1409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          mInterpreterSet.remove(interpreter);
1419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          mmDiscoveredInterpreters.remove(packageName);
1429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          notifyConfigurationObservers();
1439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
1449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      });
1459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // We require that there's only one interpreter provider per APK.
1489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private Interpreter buildInterpreter(String packageName) {
1499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      PackageInfo packInfo;
1509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      try {
1519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        packInfo = mmPackageManager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
1529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } catch (NameNotFoundException e) {
1539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        throw new RuntimeException("Package '" + packageName + "' not found.");
1549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      ProviderInfo provider = packInfo.providers[0];
1569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Map<String, String> interpreterMap =
1589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          getMap(provider, InterpreterConstants.PROVIDER_PROPERTIES);
1599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (interpreterMap == null) {
1609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        Log.e("Null interpreter map for: " + packageName);
1619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return null;
1629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Map<String, String> environmentMap =
1649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          getMap(provider, InterpreterConstants.PROVIDER_ENVIRONMENT_VARIABLES);
1659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (environmentMap == null) {
1669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        throw new RuntimeException("Null environment map for: " + packageName);
1679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Map<String, String> argumentsMap = getMap(provider, InterpreterConstants.PROVIDER_ARGUMENTS);
1699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (argumentsMap == null) {
1709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        throw new RuntimeException("Null arguments map for: " + packageName);
1719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return Interpreter.buildFromMaps(interpreterMap, environmentMap, argumentsMap);
1739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    private Map<String, String> getMap(ProviderInfo provider, String name) {
1769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Uri uri = Uri.parse("content://" + provider.authority + "/" + name);
1779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Cursor cursor = mmResolver.query(uri, null, null, null, null);
1789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (cursor == null) {
1799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return null;
1809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      cursor.moveToFirst();
1829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // Use LinkedHashMap so that order is maintained (important for position CLI arguments).
1839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Map<String, String> map = new LinkedHashMap<String, String>();
1849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      for (int i = 0; i < cursor.getColumnCount(); i++) {
1859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        map.put(cursor.getColumnName(i), cursor.getString(i));
1869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return map;
1889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    @Override
1919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    public void onReceive(Context context, Intent intent) {
1929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      final String action = intent.getAction();
1939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      final String packageName = intent.getData().getSchemeSpecificPart();
1949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (action.equals(InterpreterConstants.ACTION_INTERPRETER_ADDED)) {
1959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        mmExecutor.execute(new Runnable() {
1969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          @Override
1979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          public void run() {
1989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            addInterpreter(packageName);
1999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            notifyConfigurationObservers();
2009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
2019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        });
2029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } else if (action.equals(InterpreterConstants.ACTION_INTERPRETER_REMOVED)
2039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          || action.equals(Intent.ACTION_PACKAGE_REMOVED)
2049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          || action.equals(Intent.ACTION_PACKAGE_REPLACED)
2059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
2069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        remove(packageName);
2079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public InterpreterConfiguration(Context context) {
2139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mContext = context;
2149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mInterpreterSet = new CopyOnWriteArraySet<Interpreter>();
2159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mInterpreterSet.add(new ShellInterpreter());
2169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mObserverSet = new CopyOnWriteArraySet<ConfigurationObserver>();
2179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    IntentFilter filter = new IntentFilter();
2189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addAction(InterpreterConstants.ACTION_INTERPRETER_ADDED);
2199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addAction(InterpreterConstants.ACTION_INTERPRETER_REMOVED);
2209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
2219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
2229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
2239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    filter.addDataScheme("package");
2249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mListener = new InterpreterListener(mContext);
2259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mContext.registerReceiver(mListener, filter);
2269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void startDiscovering() {
2299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mListener.discoverAll();
2309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void startDiscovering(String mime) {
2339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mListener.discoverForType(mime);
2349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public boolean isDiscoveryComplete() {
2379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mIsDiscoveryComplete;
2389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void registerObserver(ConfigurationObserver observer) {
2419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (observer != null) {
2429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mObserverSet.add(observer);
2439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void unregisterObserver(ConfigurationObserver observer) {
2479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (observer != null) {
2489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mObserverSet.remove(observer);
2499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Returns the list of all known interpreters.
2549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
2559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public List<? extends Interpreter> getSupportedInterpreters() {
2569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return new ArrayList<Interpreter>(mInterpreterSet);
2579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Returns the list of all installed interpreters.
2619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
2629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public List<Interpreter> getInstalledInterpreters() {
2639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    List<Interpreter> interpreters = new ArrayList<Interpreter>();
2649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    for (Interpreter i : mInterpreterSet) {
2659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (i.isInstalled()) {
2669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        interpreters.add(i);
2679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return interpreters;
2709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Returns the list of interpreters that support interactive mode execution.
2749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
2759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public List<Interpreter> getInteractiveInterpreters() {
2769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    List<Interpreter> interpreters = new ArrayList<Interpreter>();
2779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    for (Interpreter i : mInterpreterSet) {
2789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (i.isInstalled() && i.hasInteractiveMode()) {
2799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        interpreters.add(i);
2809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return interpreters;
2839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Returns the interpreter matching the provided name or null if no interpreter was found.
2879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
2889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public Interpreter getInterpreterByName(String interpreterName) {
2899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    for (Interpreter i : mInterpreterSet) {
2909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (i.getName().equals(interpreterName)) {
2919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return i;
2929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return null;
2959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Returns the correct interpreter for the provided script name based on the script's extension or
2999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * null if no interpreter was found.
3009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
3019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public Interpreter getInterpreterForScript(String scriptName) {
3029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    int dotIndex = scriptName.lastIndexOf('.');
3039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (dotIndex == -1) {
3049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return null;
3059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    String ext = scriptName.substring(dotIndex);
3079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    for (Interpreter i : mInterpreterSet) {
3089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (i.getExtension().equals(ext)) {
3099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return i;
3109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
3119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return null;
3139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li}
315