/**
   * Check for any internal configuration (primarily build-time) errors.
   *
   * @return Returns true if ready to rock.
   */
  private boolean checkInternalConfiguration() {
    // Check KEYCODE name array, make sure it's up to date.

    String lastKeyName = null;
    try {
      lastKeyName = MonkeySourceRandom.getLastKeyName();
    } catch (RuntimeException e) {
    }
    if (!"TAG_LAST_KEYCODE".equals(lastKeyName)) {
      System.err.println("** Error: Key names array malformed (internal error).");
      return false;
    }

    return true;
  }
  /**
   * Run the command!
   *
   * @param args The command-line arguments
   * @return Returns a posix-style result code. 0 for no error.
   */
  private int run(String[] args) {
    // Super-early debugger wait
    for (String s : args) {
      if ("--wait-dbg".equals(s)) {
        Debug.waitForDebugger();
      }
    }

    // Default values for some command-line options
    mVerbose = 0;
    mCount = 1000;
    mSeed = 0;
    mThrottle = 0;

    // prepare for command-line processing
    mArgs = args;
    mNextArg = 0;

    // set a positive value, indicating none of the factors is provided yet
    for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
      mFactors[i] = 1.0f;
    }

    if (!processOptions()) {
      return -1;
    }

    if (!loadPackageLists()) {
      return -1;
    }

    // now set up additional data in preparation for launch
    if (mMainCategories.size() == 0) {
      mMainCategories.add(Intent.CATEGORY_LAUNCHER);
      mMainCategories.add(Intent.CATEGORY_MONKEY);
    }

    if (mVerbose > 0) {
      System.out.println(":Monkey: seed=" + mSeed + " count=" + mCount);
      if (mValidPackages.size() > 0) {
        Iterator<String> it = mValidPackages.iterator();
        while (it.hasNext()) {
          System.out.println(":AllowPackage: " + it.next());
        }
      }
      if (mInvalidPackages.size() > 0) {
        Iterator<String> it = mInvalidPackages.iterator();
        while (it.hasNext()) {
          System.out.println(":DisallowPackage: " + it.next());
        }
      }
      if (mMainCategories.size() != 0) {
        Iterator<String> it = mMainCategories.iterator();
        while (it.hasNext()) {
          System.out.println(":IncludeCategory: " + it.next());
        }
      }
    }

    if (!checkInternalConfiguration()) {
      return -2;
    }

    if (!getSystemInterfaces()) {
      return -3;
    }

    if (!getMainApps()) {
      return -4;
    }

    mRandom = new SecureRandom();
    mRandom.setSeed((mSeed == 0) ? -1 : mSeed);

    if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
      // script mode, ignore other options
      mEventSource =
          new MonkeySourceScript(
              mRandom,
              mScriptFileNames.get(0),
              mThrottle,
              mRandomizeThrottle,
              mProfileWaitTime,
              mDeviceSleepTime);
      mEventSource.setVerbose(mVerbose);

      mCountEvents = false;
    } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
      if (mSetupFileName != null) {
        mEventSource =
            new MonkeySourceRandomScript(
                mSetupFileName,
                mScriptFileNames,
                mThrottle,
                mRandomizeThrottle,
                mRandom,
                mProfileWaitTime,
                mDeviceSleepTime,
                mRandomizeScript);
        mCount++;
      } else {
        mEventSource =
            new MonkeySourceRandomScript(
                mScriptFileNames,
                mThrottle,
                mRandomizeThrottle,
                mRandom,
                mProfileWaitTime,
                mDeviceSleepTime,
                mRandomizeScript);
      }
      mEventSource.setVerbose(mVerbose);
      mCountEvents = false;
    } else if (mServerPort != -1) {
      try {
        mEventSource = new MonkeySourceNetwork(mServerPort);
      } catch (IOException e) {
        System.out.println("Error binding to network socket.");
        return -5;
      }
      mCount = Integer.MAX_VALUE;
    } else {
      // random source by default
      if (mVerbose >= 2) { // check seeding performance
        System.out.println("// Seeded: " + mSeed);
      }
      mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
      mEventSource.setVerbose(mVerbose);
      // set any of the factors that has been set
      for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
        if (mFactors[i] <= 0.0f) {
          ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
        }
      }

      // in random mode, we start with a random activity
      ((MonkeySourceRandom) mEventSource).generateActivity();
    }

    // validate source generator
    if (!mEventSource.validate()) {
      return -5;
    }

    // If we're profiling, do it immediately before/after the main monkey
    // loop
    if (mGenerateHprof) {
      signalPersistentProcesses();
    }

    mNetworkMonitor.start();
    int crashedAtCycle = runMonkeyCycles();
    mNetworkMonitor.stop();

    synchronized (this) {
      if (mRequestAnrTraces) {
        reportAnrTraces();
        mRequestAnrTraces = false;
      }
      if (mRequestAnrBugreport) {
        System.out.println("Print the anr report");
        getBugreport("anr_" + mReportProcessName + "_");
        mRequestAnrBugreport = false;
      }
      if (mRequestAppCrashBugreport) {
        getBugreport("app_crash" + mReportProcessName + "_");
        mRequestAppCrashBugreport = false;
      }
      if (mRequestDumpsysMemInfo) {
        reportDumpsysMemInfo();
        mRequestDumpsysMemInfo = false;
      }
    }

    if (mGenerateHprof) {
      signalPersistentProcesses();
      if (mVerbose > 0) {
        System.out.println("// Generated profiling reports in /data/misc");
      }
    }

    try {
      mAm.setActivityController(null);
      mNetworkMonitor.unregister(mAm);
    } catch (RemoteException e) {
      // just in case this was latent (after mCount cycles), make sure
      // we report it
      if (crashedAtCycle >= mCount) {
        crashedAtCycle = mCount - 1;
      }
    }

    // report dropped event stats
    if (mVerbose > 0) {
      System.out.print(":Dropped: keys=");
      System.out.print(mDroppedKeyEvents);
      System.out.print(" pointers=");
      System.out.print(mDroppedPointerEvents);
      System.out.print(" trackballs=");
      System.out.print(mDroppedTrackballEvents);
      System.out.print(" flips=");
      System.out.println(mDroppedFlipEvents);
    }

    // report network stats
    mNetworkMonitor.dump();

    if (crashedAtCycle < mCount - 1) {
      System.err.println(
          "** System appears to have crashed at event "
              + crashedAtCycle
              + " of "
              + mCount
              + " using seed "
              + mSeed);
      return crashedAtCycle;
    } else {
      if (mVerbose > 0) {
        System.out.println("// Monkey finished");
      }
      return 0;
    }
  }