maestro icon indicating copy to clipboard operation
maestro copied to clipboard

Fix crash in addMedia on Android 28 and below

Open dosukoi-android opened this issue 1 year ago • 6 comments

Proposed changes

I fixed an issue where the addMedia function was crashing on Android 28 and below. The crash was caused by the lack of declaration and permission for WRITE_EXTERNAL_STORAGE, which is required on these Android versions.

To resolve this, I added the WRITE_EXTERNAL_STORAGE permission declaration and implemented a process to grant the permission using adb shell pm. With these changes, the crash in addMedia on Android 28 and below has been resolved.

Testing

I have confirmed the success of running maestro test on emulators for Android versions 24 to 28.

Issues fixed

  • https://github.com/mobile-dev-inc/maestro/issues/1821

dosukoi-android avatar Sep 17 '24 17:09 dosukoi-android

Please, merge. I need it! @amanjeetsingh150

ABogdanovas avatar Oct 10 '24 13:10 ABogdanovas

Hey folks, this would go in 1.40.0 version (next release)

amanjeetsingh150 avatar Oct 16 '24 10:10 amanjeetsingh150

@amanjeetsingh150 Any reason this has not been merged?

ubuntudroid avatar Feb 18 '25 17:02 ubuntudroid

I see the same thing happening randomly on API level 36.1.

ubuntudroid avatar Nov 21 '25 18:11 ubuntudroid

Rebased

Fishbowler avatar Nov 24 '25 21:11 Fishbowler

I gave this a whirl on an API 34 device, but it didn't go well.

Flow:

appId: com.nonexistent.app
---
- addMedia: 
    files:
      - maestro.png
Maestro Logs:
22:23:04.810 [ INFO] maestro.cli.report.TestDebugReporter.logSystemInfo: Debug output path: /Users/danc/.maestro/tests/2025-11-24_222304
22:23:04.811 [ INFO] MAESTRO.logSystemInfo: ---- System Info ----
22:23:04.811 [ INFO] MAESTRO.logSystemInfo: Maestro Version: 2.0.10
22:23:04.812 [ INFO] MAESTRO.logSystemInfo: CI: Undefined
22:23:04.812 [ INFO] MAESTRO.logSystemInfo: OS Name: Mac OS X
22:23:04.812 [ INFO] MAESTRO.logSystemInfo: OS Version: 26.1
22:23:04.812 [ INFO] MAESTRO.logSystemInfo: Architecture: aarch64
22:23:04.812 [ INFO] MAESTRO.logSystemInfo: Java Version: 17
22:23:04.856 [ INFO] MAESTRO.logSystemInfo: Xcode Version: 16.4
22:23:05.001 [ INFO] MAESTRO.logSystemInfo: Flutter Version: 3.35.1
22:23:05.034 [DEBUG] io.micrometer.common.util.internal.logging.InternalLoggerFactory.newDefaultFactory: Using SLF4J as the default logging framework
22:23:05.151 [ INFO] MAESTRO.logSystemInfo: Flutter Channel: stable
22:23:05.151 [ INFO] MAESTRO.logSystemInfo: ---------------------
22:23:07.321 [ INFO] maestro.cli.command.TestCommand.runShardSuite: [shard 1] Selected device emulator-5554 using port 7001 with execution plan ExecutionPlan(flowsToRun=[/Users/danc/git/maestro/maestro-examples/addMedia/flow.yaml], sequence=FlowSequence(flows=[], continueOnFailure=true), workspaceConfig=WorkspaceConfig(flows=null, includeTags=null, excludeTags=null, local=null, executionOrder=null, baselineBranch=null, notifications=null, disableRetries=false, platform=PlatformConfiguration(android=AndroidConfiguration(disableAnimations=false), ios=IOSConfiguration(disableAnimations=false, snapshotKeyHonorModalViews=null)), testOutputDir=null))
22:23:09.690 [ INFO] maestro.cli.runner.TestRunner.runSingle$lambda$0: Running flow flow.yaml...
22:23:09.707 [ INFO] maestro.Maestro.cachedDeviceInfo_delegate$lambda$0: Getting device info
22:23:09.851 [ INFO] maestro.Maestro.cachedDeviceInfo_delegate$lambda$0: Got device info: DeviceInfo(platform=ANDROID, widthPixels=1080, heightPixels=2400, widthGrid=1080, heightGrid=2400)
22:23:09.880 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$0: Define variables RUNNING
22:23:09.883 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$11: Define variables metadata CommandMetadata(numberOfRuns=null, evaluatedCommand=MaestroCommand(defineVariablesCommand=DefineVariablesCommand(env={MAESTRO_FILENAME=flow}, label=null, optional=false)), logMessages=[], insight=Insight(message=, level=NONE), aiReasoning=null, labeledCommand=null)
22:23:09.884 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$2: Define variables COMPLETED
22:23:09.884 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$0: Apply configuration RUNNING
22:23:09.885 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$11: Apply configuration metadata CommandMetadata(numberOfRuns=null, evaluatedCommand=MaestroCommand(applyConfigurationCommand=ApplyConfigurationCommand(config=MaestroConfig(appId=com.nonexistent.app, name=null, tags=[], ext={}, onFlowStart=null, onFlowComplete=null), label=null, optional=false)), logMessages=[], insight=Insight(message=, level=NONE), aiReasoning=null, labeledCommand=null)
22:23:09.885 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$2: Apply configuration COMPLETED
22:23:09.885 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$0: Adding media files(1) to the device RUNNING
22:23:09.886 [ INFO] maestro.cli.runner.MaestroCommandRunner.runCommands$lambda$11: Adding media files(1) to the device metadata CommandMetadata(numberOfRuns=null, evaluatedCommand=MaestroCommand(addMediaCommand=AddMediaCommand(mediaPaths=[/Users/danc/git/maestro/maestro-examples/addMedia/maestro.png], label=null, optional=false)), logMessages=[], insight=Insight(message=, level=NONE), aiReasoning=null, labeledCommand=null)
22:23:09.887 [ INFO] maestro.drivers.AndroidDriver.addMedia$lambda$64: [Start] Adding media files
22:23:09.929 [ERROR] maestro.orchestra.Orchestra.executeCommands: [Command execution] CommandFailed: UNKNOWN
22:23:09.930 [ INFO] maestro.Maestro.takeScreenshot: Taking screenshot
22:23:09.931 [TRACE] maestro.utils.ScreenshotUtils.takeScreenshot: Taking screenshot to output sink
22:23:10.077 [ERROR] maestro.cli.runner.TestRunner.runCatching-ta8aW1Q: Failed to run flow
io.grpc.StatusRuntimeException: UNKNOWN
	at io.grpc.Status.asRuntimeException(Status.java:537) ~[grpc-api-1.57.2.jar:1.57.2]
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:491) ~[grpc-stub-1.57.2.jar:1.57.2]
	at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:563) ~[grpc-core-1.50.2.jar:1.50.2]
	at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:70) ~[grpc-core-1.50.2.jar:1.50.2]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:744) ~[grpc-core-1.50.2.jar:1.50.2]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:723) ~[grpc-core-1.50.2.jar:1.50.2]
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) ~[grpc-core-1.50.2.jar:1.50.2]
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133) ~[grpc-core-1.50.2.jar:1.50.2]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?]
	at java.base/java.lang.Thread.run(Thread.java:840) ~[?:?]
ADB Logs:
11-24 22:23:06.031 20295 20358 E MediaStorage: Permission android.permission.WRITE_EXTERNAL_STORAGE not granted
11-24 22:23:06.032 20295 20358 E SerializingExecutor: Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1MessagesAvailable@1cc04c
11-24 22:23:06.032 20295 20358 E SerializingExecutor: java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.OutputStream.write(byte[])' on a null object reference
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at com.google.protobuf.ByteString$LiteralByteString.writeTo(ByteString.java:1459)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at dev.mobile.maestro.Service$addMedia$1.onNext(MaestroDriverService.kt:272)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at dev.mobile.maestro.Service$addMedia$1.onNext(MaestroDriverService.kt:259)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.stub.ServerCalls$StreamingServerCallHandler$StreamingServerCallListener.onMessage(ServerCalls.java:262)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailableInternal(ServerCallImpl.java:333)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailable(ServerCallImpl.java:316)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1MessagesAvailable.runInContext(ServerImpl.java:835)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
11-24 22:23:06.032 20295 20358 E SerializingExecutor: 	at java.lang.Thread.run(Thread.java:1012)

Fishbowler avatar Nov 24 '25 22:11 Fishbowler