[path_provider] Switch Android to an internal method channel (#4617)

diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md
index 628bd44..f9d3543 100644
--- a/packages/path_provider/path_provider_android/CHANGELOG.md
+++ b/packages/path_provider/path_provider_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.10
+
+* Switches to a package-internal implementation of the platform interface.
+
 ## 2.0.9
 
 * Updates Android compileSdkVersion to 31.
diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
index 3ff2416..278ff58 100644
--- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
+++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
@@ -153,7 +153,7 @@
   public PathProviderPlugin() {}
 
   private void setup(BinaryMessenger messenger, Context context) {
-    String channelName = "plugins.flutter.io/path_provider";
+    String channelName = "plugins.flutter.io/path_provider_android";
     // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147
     // becomes available on the stable branch.
     try {
diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart
new file mode 100644
index 0000000..b0f3808
--- /dev/null
+++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+
+/// The Android implementation of [PathProviderPlatform].
+class PathProviderAndroid extends PathProviderPlatform {
+  /// The method channel used to interact with the native platform.
+  @visibleForTesting
+  MethodChannel methodChannel =
+      const MethodChannel('plugins.flutter.io/path_provider_android');
+
+  /// Registers this class as the default instance of [PathProviderPlatform].
+  static void registerWith() {
+    PathProviderPlatform.instance = PathProviderAndroid();
+  }
+
+  @override
+  Future<String?> getTemporaryPath() {
+    return methodChannel.invokeMethod<String>('getTemporaryDirectory');
+  }
+
+  @override
+  Future<String?> getApplicationSupportPath() {
+    return methodChannel.invokeMethod<String>('getApplicationSupportDirectory');
+  }
+
+  @override
+  Future<String?> getLibraryPath() {
+    throw UnsupportedError('getLibraryPath is not supported on Android');
+  }
+
+  @override
+  Future<String?> getApplicationDocumentsPath() {
+    return methodChannel
+        .invokeMethod<String>('getApplicationDocumentsDirectory');
+  }
+
+  @override
+  Future<String?> getExternalStoragePath() {
+    return methodChannel.invokeMethod<String>('getStorageDirectory');
+  }
+
+  @override
+  Future<List<String>?> getExternalCachePaths() {
+    return methodChannel
+        .invokeListMethod<String>('getExternalCacheDirectories');
+  }
+
+  @override
+  Future<List<String>?> getExternalStoragePaths({
+    StorageDirectory? type,
+  }) async {
+    return methodChannel.invokeListMethod<String>(
+      'getExternalStorageDirectories',
+      <String, dynamic>{'type': type?.index},
+    );
+  }
+
+  @override
+  Future<String?> getDownloadsPath() {
+    throw UnsupportedError('getDownloadsPath is not supported on Android');
+  }
+}
diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml
index 09b26a5..5664ebf 100644
--- a/packages/path_provider/path_provider_android/pubspec.yaml
+++ b/packages/path_provider/path_provider_android/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Android implementation of the path_provider plugin.
 repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
-version: 2.0.9
+version: 2.0.10
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
@@ -15,11 +15,12 @@
       android:
         package: io.flutter.plugins.pathprovider
         pluginClass: PathProviderPlugin
+        dartPluginClass: PathProviderAndroid
 
 dependencies:
   flutter:
     sdk: flutter
-  path_provider_platform_interface: ^2.0.0
+  path_provider_platform_interface: ^2.0.1
 
 dev_dependencies:
   flutter_driver:
diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart
new file mode 100644
index 0000000..d2f9682
--- /dev/null
+++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart
@@ -0,0 +1,136 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:path_provider_android/path_provider_android.dart';
+import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+  const String kTemporaryPath = 'temporaryPath';
+  const String kApplicationSupportPath = 'applicationSupportPath';
+  const String kLibraryPath = 'libraryPath';
+  const String kApplicationDocumentsPath = 'applicationDocumentsPath';
+  const String kExternalCachePaths = 'externalCachePaths';
+  const String kExternalStoragePaths = 'externalStoragePaths';
+  const String kDownloadsPath = 'downloadsPath';
+
+  group('PathProviderAndroid', () {
+    late PathProviderAndroid pathProvider;
+    final List<MethodCall> log = <MethodCall>[];
+
+    setUp(() async {
+      pathProvider = PathProviderAndroid();
+      TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+          .setMockMethodCallHandler(pathProvider.methodChannel,
+              (MethodCall methodCall) async {
+        log.add(methodCall);
+        switch (methodCall.method) {
+          case 'getTemporaryDirectory':
+            return kTemporaryPath;
+          case 'getApplicationSupportDirectory':
+            return kApplicationSupportPath;
+          case 'getLibraryDirectory':
+            return kLibraryPath;
+          case 'getApplicationDocumentsDirectory':
+            return kApplicationDocumentsPath;
+          case 'getExternalStorageDirectories':
+            return <String>[kExternalStoragePaths];
+          case 'getExternalCacheDirectories':
+            return <String>[kExternalCachePaths];
+          case 'getDownloadsDirectory':
+            return kDownloadsPath;
+          default:
+            return null;
+        }
+      });
+    });
+
+    tearDown(() {
+      log.clear();
+    });
+
+    test('getTemporaryPath', () async {
+      final String? path = await pathProvider.getTemporaryPath();
+      expect(
+        log,
+        <Matcher>[isMethodCall('getTemporaryDirectory', arguments: null)],
+      );
+      expect(path, kTemporaryPath);
+    });
+
+    test('getApplicationSupportPath', () async {
+      final String? path = await pathProvider.getApplicationSupportPath();
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall('getApplicationSupportDirectory', arguments: null)
+        ],
+      );
+      expect(path, kApplicationSupportPath);
+    });
+
+    test('getLibraryPath fails', () async {
+      try {
+        await pathProvider.getLibraryPath();
+        fail('should throw UnsupportedError');
+      } catch (e) {
+        expect(e, isUnsupportedError);
+      }
+    });
+
+    test('getApplicationDocumentsPath', () async {
+      final String? path = await pathProvider.getApplicationDocumentsPath();
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall('getApplicationDocumentsDirectory', arguments: null)
+        ],
+      );
+      expect(path, kApplicationDocumentsPath);
+    });
+
+    test('getExternalCachePaths succeeds', () async {
+      final List<String>? result = await pathProvider.getExternalCachePaths();
+      expect(
+        log,
+        <Matcher>[isMethodCall('getExternalCacheDirectories', arguments: null)],
+      );
+      expect(result!.length, 1);
+      expect(result.first, kExternalCachePaths);
+    });
+
+    for (final StorageDirectory? type in <StorageDirectory?>[
+      null,
+      ...StorageDirectory.values
+    ]) {
+      test('getExternalStoragePaths (type: $type) android succeeds', () async {
+        final List<String>? result =
+            await pathProvider.getExternalStoragePaths(type: type);
+        expect(
+          log,
+          <Matcher>[
+            isMethodCall(
+              'getExternalStorageDirectories',
+              arguments: <String, dynamic>{'type': type?.index},
+            )
+          ],
+        );
+
+        expect(result!.length, 1);
+        expect(result.first, kExternalStoragePaths);
+      });
+    } // end of for-loop
+
+    test('getDownloadsPath fails', () async {
+      try {
+        await pathProvider.getDownloadsPath();
+        fail('should throw UnsupportedError');
+      } catch (e) {
+        expect(e, isUnsupportedError);
+      }
+    });
+  });
+}