content: /**
   * Copyright 2014 Zhenguo Jin
   * <p/>
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * <p/>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p/>
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.rsen.github.androidutils;
  
  import android.annotation.TargetApi;
  import android.app.ActivityManager;
  import android.app.ActivityManager.MemoryInfo;
  import android.app.ActivityManager.RunningAppProcessInfo;
  import android.app.ActivityManager.RunningServiceInfo;
  import android.app.ActivityManager.RunningTaskInfo;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.content.pm.Signature;
  import android.net.Uri;
  import android.os.Build;
  import android.telephony.TelephonyManager;
  import android.text.TextUtils;
  import android.util.Log;
  
  import com.angcyo.rsen.BuildConfig;
  
  import java.io.ByteArrayInputStream;
  import java.io.File;
  import java.io.FileFilter;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.security.MessageDigest;
  import java.security.cert.CertificateException;
  import java.security.cert.CertificateFactory;
  import java.security.cert.X509Certificate;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.UUID;
  import java.util.regex.Pattern;
  
  import javax.security.auth.x500.X500Principal;
  
  /**
   * APP工具类
   * APP相关信息工具类。获取版本信息
   *
   * @author jingle1267@163.com
   */
  public final class AppUtils {
  
      private static final boolean DEBUG = true;
      private static final String TAG = "AppUtils";
  
      /**
       * Don't let anyone instantiate this class.
       */
      private AppUtils() {
          throw new Error("Do not need instantiate!");
      }
  
      /**
       * 得到软件版本号
       *
       * @param context 上下文
       * @return 当前版本Code
       */
      public static int getVerCode(Context context) {
          int verCode = -1;
          try {
              String packageName = context.getPackageName();
              verCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode;
          } catch (NameNotFoundException e) {
              e.printStackTrace();
          }
          return verCode;
      }
  
      /**
       * 得到软件显示版本信息
       *
       * @param context 上下文
       * @return 当前版本信息
       */
      public static String getVerName(Context context) {
          String verName = "";
          try {
              String packageName = context.getPackageName();
              verName = context.getPackageManager().getPackageInfo(packageName, 0).versionName;
          } catch (NameNotFoundException e) {
              e.printStackTrace();
          }
          return verName;
      }
  
      /**
       * 安装apk
       *
       * @param context 上下文
       * @param file    APK文件
       */
      public static void installApk(Context context, File file) {
          Intent intent = new Intent();
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          intent.setAction(Intent.ACTION_VIEW);
          intent.setDataAndType(Uri.fromFile(file),
                  "application/vnd.android.package-archive");
          context.startActivity(intent);
      }
  
      /**
       * 安装apk
       *
       * @param context 上下文
       * @param file    APK文件uri
       */
      public static void installApk(Context context, Uri file) {
          Intent intent = new Intent();
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          intent.setAction(Intent.ACTION_VIEW);
          intent.setDataAndType(file, "application/vnd.android.package-archive");
          context.startActivity(intent);
      }
  
      /**
       * 卸载apk
       *
       * @param context     上下文
       * @param packageName 包名
       */
      public static void uninstallApk(Context context, String packageName) {
          Intent intent = new Intent(Intent.ACTION_DELETE);
          Uri packageURI = Uri.parse("package:" + packageName);
          intent.setData(packageURI);
          context.startActivity(intent);
      }
  
      /**
       * 检测服务是否运行
       *
       * @param context   上下文
       * @param className 类名
       * @return 是否运行的状态
       */
      public static boolean isServiceRunning(Context context, String className) {
          boolean isRunning = false;
          ActivityManager activityManager = (ActivityManager) context
                  .getSystemService(Context.ACTIVITY_SERVICE);
          List<RunningServiceInfo> servicesList = activityManager
                  .getRunningServices(Integer.MAX_VALUE);
          for (RunningServiceInfo si : servicesList) {
              if (className.equals(si.service.getClassName())) {
                  isRunning = true;
              }
          }
          return isRunning;
      }
  
      /**
       * 停止运行服务
       *
       * @param context   上下文
       * @param className 类名
       * @return 是否执行成功
       */
      public static boolean stopRunningService(Context context, String className) {
          Intent intent_service = null;
          boolean ret = false;
          try {
              intent_service = new Intent(context, Class.forName(className));
          } catch (Exception e) {
              e.printStackTrace();
          }
          if (intent_service != null) {
              ret = context.stopService(intent_service);
          }
          return ret;
      }
  
      /**
       * 得到CPU核心数
       *
       * @return CPU核心数
       */
      public static int getNumCores() {
          try {
              File dir = new File("/sys/devices/system/cpu/");
              File[] files = dir.listFiles(new FileFilter() {
                  @Override
                  public boolean accept(File pathname) {
                      return Pattern.matches("cpu[0-9]", pathname.getName());
                  }
              });
              return files.length;
          } catch (Exception e) {
              return 1;
          }
      }
  
      /**
       * whether this process is named with processName
       *
       * @param context     上下文
       * @param processName 进程名
       * @return <ul>
       * return whether this process is named with processName
       * <li>if context is null, return false</li>
       * <li>if {@link ActivityManager#getRunningAppProcesses()} is null,
       * return false</li>
       * <li>if one process of
       * {@link ActivityManager#getRunningAppProcesses()} is equal to
       * processName, return true, otherwise return false</li>
       * </ul>
       */
      public static boolean isNamedProcess(Context context, String processName) {
          if (context == null || TextUtils.isEmpty(processName)) {
              return false;
          }
  
          int pid = android.os.Process.myPid();
          ActivityManager manager = (ActivityManager) context
                  .getSystemService(Context.ACTIVITY_SERVICE);
          List<RunningAppProcessInfo> processInfoList = manager
                  .getRunningAppProcesses();
          if (processInfoList == null) {
              return true;
          }
  
          for (RunningAppProcessInfo processInfo : manager
                  .getRunningAppProcesses()) {
              if (processInfo.pid == pid
                      && processName.equalsIgnoreCase(processInfo.processName)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * whether application is in background
       * <ul>
       * <li>need use permission android.permission.GET_TASKS in Manifest.xml</li>
       * </ul>
       *
       * @param context 上下文
       * @return if application is in background return true, otherwise return
       * false
       */
      public static boolean isApplicationInBackground(Context context) {
          ActivityManager am = (ActivityManager) context
                  .getSystemService(Context.ACTIVITY_SERVICE);
          List<RunningTaskInfo> taskList = am.getRunningTasks(1);
          if (taskList != null && !taskList.isEmpty()) {
              ComponentName topActivity = taskList.get(0).topActivity;
              if (topActivity != null
                      && !topActivity.getPackageName().equals(
                      context.getPackageName())) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * 获取应用签名
       *
       * @param context 上下文
       * @param pkgName 包名
       */
      public static String getSign(Context context, String pkgName) {
          try {
              PackageInfo pis = context.getPackageManager().getPackageInfo(
                      pkgName, PackageManager.GET_SIGNATURES);
              return hexdigest(pis.signatures[0].toByteArray());
          } catch (NameNotFoundException e) {
              e.printStackTrace();
              return null;
          }
      }
  
      /**
       * 将签名字符串转换成需要的32位签名
       *
       * @param paramArrayOfByte 签名byte数组
       * @return 32位签名字符串
       */
      private static String hexdigest(byte[] paramArrayOfByte) {
          final char[] hexDigits = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97,
                  98, 99, 100, 101, 102};
          try {
              MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");
              localMessageDigest.update(paramArrayOfByte);
              byte[] arrayOfByte = localMessageDigest.digest();
              char[] arrayOfChar = new char[32];
              for (int i = 0, j = 0; ; i++, j++) {
                  if (i >= 16) {
                      return new String(arrayOfChar);
                  }
                  int k = arrayOfByte[i];
                  arrayOfChar[j] = hexDigits[(0xF & k >>> 4)];
                  arrayOfChar[++j] = hexDigits[(k & 0xF)];
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
          return "";
      }
  
      /**
       * 清理后台进程与服务
       *
       * @param context 应用上下文对象context
       * @return 被清理的数量
       */
      @TargetApi(Build.VERSION_CODES.FROYO)
      public static int gc(Context context) {
          long i = getDeviceUsableMemory(context);
          int count = 0; // 清理掉的进程数
          ActivityManager am = (ActivityManager) context
                  .getSystemService(Context.ACTIVITY_SERVICE);
          // 获取正在运行的service列表
          List<RunningServiceInfo> serviceList = am.getRunningServices(100);
          if (serviceList != null)
              for (RunningServiceInfo service : serviceList) {
                  if (service.pid == android.os.Process.myPid())
                      continue;
                  try {
                      android.os.Process.killProcess(service.pid);
                      count++;
                  } catch (Exception e) {
                      e.getStackTrace();
                  }
              }
  
          // 获取正在运行的进程列表
          List<RunningAppProcessInfo> processList = am.getRunningAppProcesses();
          if (processList != null)
              for (RunningAppProcessInfo process : processList) {
                  // 一般数值大于RunningAppProcessInfo.IMPORTANCE_SERVICE的进程都长时间没用或者空进程了
                  // 一般数值大于RunningAppProcessInfo.IMPORTANCE_VISIBLE的进程都是非可见进程，也就是在后台运行着
                  if (process.importance > RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
                      // pkgList 得到该进程下运行的包名
                      String[] pkgList = process.pkgList;
                      for (String pkgName : pkgList) {
                          if (DEBUG) {
                              LogUtils.d(TAG, "======正在杀死包名：" + pkgName);
                          }
                          try {
                              am.killBackgroundProcesses(pkgName);
                              count++;
                          } catch (Exception e) { // 防止意外发生
                              e.getStackTrace();
                          }
                      }
                  }
              }
          if (DEBUG) {
              LogUtils.d(TAG, "清理了" + (getDeviceUsableMemory(context) - i) + "M内存");
          }
          return count;
      }
  
      /**
       * 获取设备的可用内存大小
       *
       * @param context 应用上下文对象context
       * @return 当前内存大小
       */
      public static int getDeviceUsableMemory(Context context) {
          ActivityManager am = (ActivityManager) context
                  .getSystemService(Context.ACTIVITY_SERVICE);
          MemoryInfo mi = new MemoryInfo();
          am.getMemoryInfo(mi);
          // 返回当前系统的可用内存
          return (int) (mi.availMem / (1024 * 1024));
      }
  
      /**
       * 获取系统中所有的应用
       *
       * @param context 上下文
       * @return 应用信息List
       */
      public static List<PackageInfo> getAllApps(Context context) {
  
          List<PackageInfo> apps = new ArrayList<PackageInfo>();
          PackageManager pManager = context.getPackageManager();
          List<PackageInfo> paklist = pManager.getInstalledPackages(0);
          for (int i = 0; i < paklist.size(); i++) {
              PackageInfo pak = paklist.get(i);
              if ((pak.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {
                  // customs applications
                  apps.add(pak);
              }
          }
          return apps;
      }
  
      /**
       * 获取手机系统SDK版本
       *
       * @return 如API 17 则返回 17
       */
      @TargetApi(Build.VERSION_CODES.DONUT)
      public static int getSDKVersion() {
          return Build.VERSION.SDK_INT;
      }
  
      /**
       * 是否Dalvik模式
       *
       * @return 结果
       */
      public static boolean isDalvik() {
          return "Dalvik".equals(getCurrentRuntimeValue());
      }
  
      /**
       * 是否ART模式
       *
       * @return 结果
       */
      public static boolean isART() {
          String currentRuntime = getCurrentRuntimeValue();
          return "ART".equals(currentRuntime) || "ART debug build".equals(currentRuntime);
      }
  
      /**
       * 获取手机当前的Runtime
       *
       * @return 正常情况下可能取值Dalvik, ART, ART debug build;
       */
      public static String getCurrentRuntimeValue() {
          try {
              Class<?> systemProperties = Class.forName("android.os.SystemProperties");
              try {
                  Method get = systemProperties.getMethod("get",
                          String.class, String.class);
                  if (get == null) {
                      return "WTF?!";
                  }
                  try {
                      final String value = (String) get.invoke(
                              systemProperties, "persist.sys.dalvik.vm.lib",
                          /* Assuming default is */"Dalvik");
                      if ("libdvm.so".equals(value)) {
                          return "Dalvik";
                      } else if ("libart.so".equals(value)) {
                          return "ART";
                      } else if ("libartd.so".equals(value)) {
                          return "ART debug build";
                      }
  
                      return value;
                  } catch (IllegalAccessException e) {
                      return "IllegalAccessException";
                  } catch (IllegalArgumentException e) {
                      return "IllegalArgumentException";
                  } catch (InvocationTargetException e) {
                      return "InvocationTargetException";
                  }
              } catch (NoSuchMethodException e) {
                  return "SystemProperties.get(String key, String def) method is not found";
              }
          } catch (ClassNotFoundException e) {
              return "SystemProperties class is not found";
          }
      }
  
      private final static X500Principal DEBUG_DN = new X500Principal(
              "CN=Android Debug,O=Android,C=US");
  
      /**
       * 检测当前应用是否是Debug版本
       *
       * @param ctx
       * @return
       */
      public static boolean isDebuggable(Context ctx) {
          boolean debuggable = false;
          try {
              PackageInfo pinfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES);
              Signature signatures[] = pinfo.signatures;
              for (int i = 0; i < signatures.length; i++) {
                  CertificateFactory cf = CertificateFactory.getInstance("X.509");
                  ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray());
                  X509Certificate cert = (X509Certificate) cf
                          .generateCertificate(stream);
                  debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
                  if (debuggable)
                      break;
              }
  
          } catch (NameNotFoundException e) {
          } catch (CertificateException e) {
          }
          return debuggable;
      }
  
      /**
       * 获取设备唯一标识
       * @param context
       * @return
       */
      public static String getUUID(Context context) {
          final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
          final String tmDevice, tmSerial, tmPhone, androidId;
          tmDevice = "" + tm.getDeviceId();
          tmSerial = "" + tm.getSimSerialNumber();
          androidId = "" + android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
  
          UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
          String uniqueId = deviceUuid.toString();
          if (BuildConfig.DEBUG) Log.d(TAG, "uuid=" + uniqueId);
  
          return uniqueId;
      }
  
      /**
       * 是否是主线程
       * @return
       */
      public static boolean isMainProcess(Context context) {
          ActivityManager am = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE));
          List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
          String mainProcessName = context.getPackageName();
          int myPid = android.os.Process.myPid();
          for (RunningAppProcessInfo info : processInfos) {
              if (info.pid == myPid && mainProcessName.equals(info.processName)) {
                  return true;
              }
          }
          return false;
      }
  
  }
  