@Override
  public ParcelFileDescriptor openDocument(
      String documentId, String mode, CancellationSignal signal) throws FileNotFoundException {
    final File file = getFileForDocId(documentId);
    final File visibleFile = getFileForDocId(documentId, true);

    final int pfdMode = ParcelFileDescriptor.parseMode(mode);
    if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
      return ParcelFileDescriptor.open(file, pfdMode);
    } else {
      try {
        // When finished writing, kick off media scanner
        return ParcelFileDescriptor.open(
            file,
            pfdMode,
            mHandler,
            new OnCloseListener() {
              @Override
              public void onClose(IOException e) {
                final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                intent.setData(Uri.fromFile(visibleFile));
                getContext().sendBroadcast(intent);
              }
            });
      } catch (IOException e) {
        throw new FileNotFoundException("Failed to open for writing: " + e);
      }
    }
  }
  @NonNull
  private ParcelFileDescriptor safeOpenFileHelper(@NonNull Uri uri, @NonNull String mode)
      throws FileNotFoundException {
    Cursor c = query(uri, new String[] {"_data"}, null, null, null);
    int count = (c != null) ? c.getCount() : 0;
    if (count != 1) {
      // If there is not exactly one result, throw an appropriate
      // exception.
      if (c != null) {
        c.close();
      }
      if (count == 0) {
        throw new FileNotFoundException("No entry for " + uri);
      }
      throw new FileNotFoundException("Multiple items at " + uri);
    }

    c.moveToFirst();
    int i = c.getColumnIndex("_data");
    String path = (i >= 0 ? c.getString(i) : null);
    c.close();

    if (path == null) {
      throw new FileNotFoundException("Column _data not found.");
    }

    File filePath = new File(path);
    try {
      // The MmsProvider shouldn't open a file that isn't MMS data, so we verify that the
      // _data path actually points to MMS data. That safeguards ourselves from callers who
      // inserted or updated a URI (more specifically the _data column) with disallowed paths.
      // TODO(afurtado): provide a more robust mechanism to avoid disallowed _data paths to
      // be inserted/updated in the first place, including via SQL injection.
      if (!filePath
          .getCanonicalPath()
          .startsWith(getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath())) {
        Log.e(
            TAG,
            "openFile: path "
                + filePath.getCanonicalPath()
                + " does not start with "
                + getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath());
        // Don't care return value
        return null;
      }
    } catch (IOException e) {
      Log.e(TAG, "openFile: create path failed " + e, e);
      return null;
    }

    int modeBits = ParcelFileDescriptor.parseMode(mode);
    return ParcelFileDescriptor.open(filePath, modeBits);
  }