private void saveSignatures(PackageInfo pkgInfo) {
   if (pkgInfo != null && pkgInfo.signatures != null) {
     int i = 0;
     for (Signature signature : pkgInfo.signatures) {
       File file =
           new File(PluginDirHelper.getPluginSignatureFile(mContext, pkgInfo.packageName, i));
       try {
         Utils.writeToFile(file, signature.toByteArray());
         Log.i(
             TAG,
             "Save %s signature of %s,md5=%s",
             pkgInfo.packageName,
             i,
             Utils.md5(signature.toByteArray()));
       } catch (Exception e) {
         e.printStackTrace();
         Log.w(TAG, "Save signatures fail", e);
         file.delete();
         Utils.deleteDir(PluginDirHelper.getPluginSignatureDir(mContext, pkgInfo.packageName));
         break;
       }
       i++;
     }
   }
 }
  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));
  }
 private Signature[] readSignatures(String packageName) {
   List<String> fils = PluginDirHelper.getPluginSignatureFiles(mContext, packageName);
   List<Signature> signatures = new ArrayList<Signature>(fils.size());
   int i = 0;
   for (String file : fils) {
     try {
       byte[] data = Utils.readFromFile(new File(file));
       if (data != null) {
         Signature sin = new Signature(data);
         signatures.add(sin);
         Log.i(
             TAG, "Read %s signature of %s,md5=%s", packageName, i, Utils.md5(sin.toByteArray()));
       } else {
         Log.i(TAG, "Read %s signature of %s FAIL", packageName, i);
         return null;
       }
       i++;
     } catch (Exception e) {
       Log.i(TAG, "Read %s signature of %s FAIL", e, packageName, i);
       return null;
     }
   }
   return signatures.toArray(new Signature[signatures.size()]);
 }
 @Override
 public boolean killBackgroundProcesses(String pluginPackageName) throws RemoteException {
   ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
   List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
   boolean success = false;
   for (RunningAppProcessInfo info : infos) {
     if (info.pkgList != null
         && Arrays.binarySearch(info.pkgList, pluginPackageName) >= 0
         && info.pid != android.os.Process.myPid()) {
       Log.i(
           TAG,
           "killBackgroundProcesses(%s),pkgList=%s,pid=%s",
           pluginPackageName,
           Arrays.toString(info.pkgList),
           info.pid);
       android.os.Process.killProcess(info.pid);
       success = true;
     }
   }
   return success;
 }
  @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;
    }
  }
  private void copyNativeLibs(Context context, String apkfile, ApplicationInfo applicationInfo)
      throws Exception {
    String nativeLibraryDir =
        PluginDirHelper.getPluginNativeLibraryDir(context, applicationInfo.packageName);
    ZipFile zipFile = null;
    try {
      zipFile = new ZipFile(apkfile);
      Enumeration<? extends ZipEntry> entries = zipFile.entries();
      Map<String, ZipEntry> libZipEntries = new HashMap<String, ZipEntry>();
      Map<String, Set<String>> soList = new HashMap<String, Set<String>>(1);
      while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();
        String name = entry.getName();
        if (name.contains("../")) {
          Log.d(TAG, "Path traversal attack prevented");
          continue;
        }
        if (name.startsWith("lib/") && !entry.isDirectory()) {
          libZipEntries.put(name, entry);
          String soName = new File(name).getName();
          Set<String> fs = soList.get(soName);
          if (fs == null) {
            fs = new TreeSet<String>();
            soList.put(soName, fs);
          }
          fs.add(name);
        }
      }

      for (String soName : soList.keySet()) {
        Log.e(TAG, "==========so name=" + soName);
        Set<String> soPaths = soList.get(soName);
        String soPath = findSoPath(soPaths);
        if (soPath != null) {
          File file = new File(nativeLibraryDir, soName);
          if (file.exists()) {
            file.delete();
          }
          InputStream in = null;
          FileOutputStream ou = null;
          try {
            in = zipFile.getInputStream(libZipEntries.get(soPath));
            ou = new FileOutputStream(file);
            byte[] buf = new byte[8192];
            int read = 0;
            while ((read = in.read(buf)) != -1) {
              ou.write(buf, 0, read);
            }
            ou.flush();
            ou.getFD().sync();
            Log.i(TAG, "copy so(%s) for %s to %s ok!", soName, soPath, file.getPath());
          } catch (Exception e) {
            if (file.exists()) {
              file.delete();
            }
            throw e;
          } finally {
            if (in != null) {
              try {
                in.close();
              } catch (Exception e) {
              }
            }
            if (ou != null) {
              try {
                ou.close();
              } catch (Exception e) {
              }
            }
          }
        }
      }
    } finally {
      if (zipFile != null) {
        try {
          zipFile.close();
        } catch (Exception e) {
        }
      }
    }
  }