@Override
    protected List<Label> doInBackground(Void... params) {
      final List<Label> allLabels = mClient.getAllLabels();

      if ((allLabels == null) || allLabels.isEmpty()) {
        return null;
      }

      final PackageManager pm = mContext.getPackageManager();

      final List<Label> candidates = new ArrayList<>(allLabels);
      ListIterator<Label> i = candidates.listIterator();

      // Iterate through the labels database, and prune labels that belong
      // to valid packages.
      while (i.hasNext()) {
        final Label l = i.next();

        // Ensure the label has a matching installed package.
        final String packageName = l.getPackageName();
        PackageInfo packageInfo;
        try {
          packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
        } catch (NameNotFoundException e) {
          // If there's no installed package, leave the label in the
          // list for removal.
          LogUtils.log(
              CustomLabelManager.class,
              Log.VERBOSE,
              "Consistency check removing label for unknown package %s.",
              packageName);
          continue;
        }

        // Ensure the signature hash of the application matches
        // the hash of the package when the label was stored.
        final String expectedHash = l.getPackageSignature();
        final String actualHash = computePackageSignatureHash(packageInfo);
        if (TextUtils.isEmpty(expectedHash)
            || TextUtils.isEmpty(actualHash)
            || !expectedHash.equals(actualHash)) {
          // If the expected or actual signature hashes aren't
          // valid, or they don't match, leave the label in the list
          // for removal.
          LogUtils.log(
              CustomLabelManager.class,
              Log.WARN,
              "Consistency check removing label due to signature mismatch " + "for package %s.",
              packageName);
          continue;
        }

        // If the label has passed all consistency checks, prune the
        // label from the list of potentials for removal.
        i.remove();
      }

      return candidates; // now containing only labels for removal
    }
            // Note this comparator is not consistent with equals in Label, we should just implement
            // compareTo there, but will wait for BrailleBack to be merged with TalkBack
            @Override
            public int compare(Label first, Label second) {
              if (first == null) {
                if (second == null) {
                  return 0;
                } else {
                  return -1;
                }
              }

              if (second == null) return 1;

              int ret = first.getPackageName().compareTo(second.getPackageName());
              if (ret != 0) return ret;

              return first.getViewName().compareTo(second.getViewName());
            }
    @Override
    protected void onPostExecute(Label result) {
      LogUtils.log(
          this, Log.VERBOSE, "LabelAddTask(%d) complete, stored as %s", hashCode(), result);
      mRequest.invokeCallback(result);

      if (result != null) {
        sendCacheRefreshIntent(result.getPackageName());
      }

      super.onPostExecute(result);
    }
  /**
   * Retrieves a {@link Label} from the label cache given a fully-qualified resource identifier
   * name.
   *
   * @param resourceName The fully-qualified resource identifier, such as
   *     "com.android.deskclock:id/analog_appwidget", as provided by {@link
   *     AccessibilityNodeInfo#getViewIdResourceName()}
   * @return The {@link Label} matching the provided identifier, or {@code null} if no such label
   *     exists or has not yet been fetched from storage
   */
  public Label getLabelForViewIdFromCache(String resourceName) {
    if (!isInitialized()) {
      return null;
    }

    Pair<String, String> parsedId = splitResourceName(resourceName);
    if (parsedId == null) {
      return null;
    }

    Label search = new Label(parsedId.first, null, parsedId.second, null, null, 0, null, 0);
    Label result = mLabelCache.ceiling(search);
    // TODO: This can be done much simplier with modifying equals in Label but want to wait
    // until BrailleBack is integrate to ensure compatibility
    if (result != null
        && search.getViewName() != null
        && search.getViewName().equals(result.getViewName())
        && search.getPackageName() != null
        && search.getPackageName().equals(result.getPackageName())) {
      return result;
    }

    return null;
  }