@Override
 public List<PermissionGroupInfo> getAllPermissionGroups(int flags) throws RemoteException {
   waitForReadyInner();
   try {
     enforcePluginFileExists();
     List<PermissionGroupInfo> list = new ArrayList<PermissionGroupInfo>();
     if (shouldNotBlockOtherInfo()) {
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         List<PermissionGroupInfo> permissionGroupInfos =
             pluginPackageParser.getPermissionGroups();
         for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
           if (!list.contains(permissionGroupInfo)) {
             list.add(permissionGroupInfo);
           }
         }
       }
     } else {
       List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         List<PermissionGroupInfo> permissionGroupInfos =
             pluginPackageParser.getPermissionGroups();
         for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
           if (!list.contains(permissionGroupInfo)
               && pkgs.contains(permissionGroupInfo.packageName)) {
             list.add(permissionGroupInfo);
           }
         }
       }
     }
     return list;
   } catch (Exception e) {
     handleException(e);
   }
   return null;
 }
 @Override
 public ProviderInfo resolveContentProvider(String name, int flags) throws RemoteException {
   waitForReadyInner();
   try {
     enforcePluginFileExists();
     if (shouldNotBlockOtherInfo()) {
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         List<ProviderInfo> providerInfos = pluginPackageParser.getProviders();
         for (ProviderInfo providerInfo : providerInfos) {
           if (TextUtils.equals(providerInfo.authority, name)) {
             return providerInfo;
           }
         }
       }
     } else {
       List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         List<ProviderInfo> providerInfos = pluginPackageParser.getProviders();
         for (ProviderInfo providerInfo : providerInfos) {
           if (TextUtils.equals(providerInfo.authority, name)
               && pkgs.contains(providerInfo.packageName)) {
             return providerInfo;
           }
         }
       }
     }
   } catch (Exception e) {
     handleException(e);
   }
   return null;
 }
  @Override
  public ApplicationInfo getApplicationInfo(String packageName, int flags) throws RemoteException {
    waitForReadyInner();
    try {
      PluginPackageParser parser = mPluginCache.get(packageName);
      if (parser != null) {
        return parser.getApplicationInfo(flags);
      }
    } catch (Exception e) {
      handleException(e);
    }

    return null;
  }
 private void enforcePluginFileExists() throws RemoteException {
   List<String> removedPkg = new ArrayList<>();
   for (String pkg : mPluginCache.keySet()) {
     PluginPackageParser parser = mPluginCache.get(pkg);
     File pluginFile = parser.getPluginFile();
     if (pluginFile != null && pluginFile.exists()) {
       // DO NOTHING
     } else {
       removedPkg.add(pkg);
     }
   }
   for (String pkg : removedPkg) {
     deletePackage(pkg, 0);
   }
 }
  private void loadAllPlugin(Context context) {
    long b = System.currentTimeMillis();
    ArrayList<File> apkfiles = new ArrayList<File>();
    File baseDir = new File(PluginDirHelper.getBaseDir(context));
    File[] dirs = baseDir.listFiles();
    for (File dir : dirs) {
      if (dir.isDirectory()) {
        File file = new File(dir, "apk/base-1.apk");
        if (file.exists()) {
          apkfiles.add(file);
        }
      }
    }

    Log.i(TAG, "Search apk cost %s ms", (System.currentTimeMillis() - b));
    b = System.currentTimeMillis();

    if (apkfiles != null && apkfiles.size() > 0) {
      for (File pluginFile : apkfiles) {
        long b1 = System.currentTimeMillis();
        try {
          PluginPackageParser pluginPackageParser = new PluginPackageParser(mContext, pluginFile);
          Signature[] signatures = readSignatures(pluginPackageParser.getPackageName());
          if (signatures == null || signatures.length <= 0) {
            pluginPackageParser.collectCertificates(0);
            PackageInfo info = pluginPackageParser.getPackageInfo(PackageManager.GET_SIGNATURES);
            saveSignatures(info);
          } else {
            mSignatureCache.put(pluginPackageParser.getPackageName(), signatures);
            pluginPackageParser.writeSignature(signatures);
          }
          if (!mPluginCache.containsKey(pluginPackageParser.getPackageName())) {
            mPluginCache.put(pluginPackageParser.getPackageName(), pluginPackageParser);
          }
        } catch (Throwable e) {
          Log.e(TAG, "parse a apk file error %s", e, pluginFile.getPath());
        } finally {
          Log.i(
              TAG,
              "Parse %s apk cost %s ms",
              pluginFile.getPath(),
              (System.currentTimeMillis() - b1));
        }
      }
    }

    Log.i(TAG, "Parse all apk cost %s ms", (System.currentTimeMillis() - b));
    b = System.currentTimeMillis();

    try {
      mActivityManagerService.onCreate(IPluginManagerImpl.this);
    } catch (Exception e) {
      Log.e(TAG, "mActivityManagerService.onCreate", e);
    }

    Log.i(TAG, "ActivityManagerService.onCreate %s ms", (System.currentTimeMillis() - b));
  }
 @Override
 public ProviderInfo getProviderInfo(ComponentName className, int flags) throws RemoteException {
   waitForReadyInner();
   try {
     String pkg = getAndCheckCallingPkg(className.getPackageName());
     if (pkg != null) {
       enforcePluginFileExists();
       PluginPackageParser parser = mPluginCache.get(className.getPackageName());
       if (parser != null) {
         return parser.getProviderInfo(className, flags);
       }
     }
   } catch (Exception e) {
     handleException(e);
   }
   return null;
 }
 @Override
 public List<ActivityInfo> getReceivers(String packageName, int flags) throws RemoteException {
   try {
     String pkg = getAndCheckCallingPkg(packageName);
     if (pkg != null) {
       PluginPackageParser parser = mPluginCache.get(packageName);
       if (parser != null) {
         return new ArrayList<ActivityInfo>(parser.getReceivers());
       }
     }
   } catch (Exception e) {
     RemoteException remoteException = new RemoteException();
     remoteException.setStackTrace(e.getStackTrace());
     throw remoteException;
   }
   return new ArrayList<ActivityInfo>(0);
 }
 @Override
 public List<IntentFilter> getReceiverIntentFilter(ActivityInfo info) throws RemoteException {
   try {
     String pkg = getAndCheckCallingPkg(info.packageName);
     if (pkg != null) {
       PluginPackageParser parser = mPluginCache.get(info.packageName);
       if (parser != null) {
         List<IntentFilter> filters = parser.getReceiverIntentFilter(info);
         if (filters != null && filters.size() > 0) {
           return new ArrayList<IntentFilter>(filters);
         }
       }
     }
     return new ArrayList<IntentFilter>(0);
   } catch (Exception e) {
     RemoteException remoteException = new RemoteException();
     remoteException.setStackTrace(e.getStackTrace());
     throw remoteException;
   }
 }
 private void dexOpt(Context hostContext, String apkfile, PluginPackageParser parser)
     throws Exception {
   String packageName = parser.getPackageName();
   String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(hostContext, packageName);
   String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(hostContext, packageName);
   ClassLoader classloader =
       new PluginClassLoader(
           apkfile, optimizedDirectory, libraryPath, ClassLoader.getSystemClassLoader());
   //        DexFile dexFile = DexFile.loadDex(apkfile,
   // PluginDirHelper.getPluginDalvikCacheFile(mContext, parser.getPackageName()), 0);
   //        Log.e(TAG, "dexFile=%s,1=%s,2=%s", dexFile, DexFile.isDexOptNeeded(apkfile),
   // DexFile.isDexOptNeeded(PluginDirHelper.getPluginDalvikCacheFile(mContext,
   // parser.getPackageName())));
 }
 @Override
 public PackageInfo getPackageInfo(String packageName, int flags) throws RemoteException {
   waitForReadyInner();
   try {
     String pkg = getAndCheckCallingPkg(packageName);
     if (pkg != null) {
       enforcePluginFileExists();
       PluginPackageParser parser = mPluginCache.get(pkg);
       if (parser != null) {
         PackageInfo packageInfo = parser.getPackageInfo(flags);
         if (packageInfo != null
             && (flags & PackageManager.GET_SIGNATURES) != 0
             && packageInfo.signatures == null) {
           packageInfo.signatures = mSignatureCache.get(packageName);
         }
         return packageInfo;
       }
     }
   } catch (Exception e) {
     handleException(e);
   }
   return null;
 }
  @Override
  public void clearApplicationUserData(String packageName, IPackageDataObserver observer)
      throws RemoteException {
    boolean success = false;
    try {
      if (TextUtils.isEmpty(packageName)) {
        return;
      }

      PluginPackageParser parser = mPluginCache.get(packageName);
      if (parser == null) {
        return;
      }
      ApplicationInfo applicationInfo = parser.getApplicationInfo(0);
      Utils.deleteDir(applicationInfo.dataDir);
      success = true;
    } catch (Exception e) {
      handleException(e);
    } finally {
      if (observer != null) {
        observer.onRemoveCompleted(packageName, success);
      }
    }
  }
 @Override
 public List<ApplicationInfo> getInstalledApplications(int flags) throws RemoteException {
   waitForReadyInner();
   try {
     enforcePluginFileExists();
     List<ApplicationInfo> infos = new ArrayList<ApplicationInfo>(mPluginCache.size());
     if (shouldNotBlockOtherInfo()) {
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         infos.add(pluginPackageParser.getApplicationInfo(flags));
       }
     } else {
       List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
       for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
         if (pkgs.contains(pluginPackageParser.getPackageName())) {
           infos.add(pluginPackageParser.getApplicationInfo(flags));
         }
       }
     }
     return infos;
   } catch (Exception e) {
     handleException(e);
   }
   return null;
 }
  @Override
  public int installPackage(String filepath, int flags) throws RemoteException {
    // install plugin
    String apkfile = null;
    try {
      PackageManager pm = mContext.getPackageManager();
      PackageInfo info = pm.getPackageArchiveInfo(filepath, 0);
      if (info == null) {
        return PackageManagerCompat.INSTALL_FAILED_INVALID_APK;
      }

      apkfile = PluginDirHelper.getPluginApkFile(mContext, info.packageName);

      if ((flags & PackageManagerCompat.INSTALL_REPLACE_EXISTING) != 0) {
        forceStopPackage(info.packageName);
        if (mPluginCache.containsKey(info.packageName)) {
          deleteApplicationCacheFiles(info.packageName, null);
        }
        new File(apkfile).delete();
        Utils.copyFile(filepath, apkfile);
        PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));
        parser.collectCertificates(0);
        PackageInfo pkgInfo =
            parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
        if (pkgInfo != null
            && pkgInfo.requestedPermissions != null
            && pkgInfo.requestedPermissions.length > 0) {
          for (String requestedPermission : pkgInfo.requestedPermissions) {
            boolean b = false;
            try {
              b = pm.getPermissionInfo(requestedPermission, 0) != null;
            } catch (NameNotFoundException e) {
            }
            if (!mHostRequestedPermission.contains(requestedPermission) && b) {
              Log.e(TAG, "No Permission %s", requestedPermission);
              new File(apkfile).delete();
              return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
            }
          }
        }
        saveSignatures(pkgInfo);
        //                if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
        //                    for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
        //                        Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s",
        // reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
        //                    }
        //                }
        copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0));
        dexOpt(mContext, apkfile, parser);
        mPluginCache.put(parser.getPackageName(), parser);
        mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
        sendInstalledBroadcast(info.packageName);
        return PackageManagerCompat.INSTALL_SUCCEEDED;
      } else {
        if (mPluginCache.containsKey(info.packageName)) {
          return PackageManagerCompat.INSTALL_FAILED_ALREADY_EXISTS;
        } else {
          forceStopPackage(info.packageName);
          new File(apkfile).delete();
          Utils.copyFile(filepath, apkfile);
          PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));
          parser.collectCertificates(0);
          PackageInfo pkgInfo =
              parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
          if (pkgInfo != null
              && pkgInfo.requestedPermissions != null
              && pkgInfo.requestedPermissions.length > 0) {
            for (String requestedPermission : pkgInfo.requestedPermissions) {
              boolean b = false;
              try {
                b = pm.getPermissionInfo(requestedPermission, 0) != null;
              } catch (NameNotFoundException e) {
              }
              if (!mHostRequestedPermission.contains(requestedPermission) && b) {
                Log.e(TAG, "No Permission %s", requestedPermission);
                new File(apkfile).delete();
                return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
              }
            }
          }
          saveSignatures(pkgInfo);
          //                    if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
          //                        for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
          //                            Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s",
          // reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
          //                        }
          //                    }

          copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0));
          dexOpt(mContext, apkfile, parser);
          mPluginCache.put(parser.getPackageName(), parser);
          mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
          sendInstalledBroadcast(info.packageName);
          return PackageManagerCompat.INSTALL_SUCCEEDED;
        }
      }
    } catch (Exception e) {
      if (apkfile != null) {
        new File(apkfile).delete();
      }
      handleException(e);
      return PackageManagerCompat.INSTALL_FAILED_INTERNAL_ERROR;
    }
  }