Your Appium vineyard harvest-log cases need camera access already denied — how do you seed that on Android and iOS?
answer
- three targets, three different answers
- revoke on one, write on the other
- one target cannot reach it at all
- seed before the app is foregrounded
basics
~20 sOn Android, mobile: changePermissions revokes the camera permission on the harvest-log package. On an Apple Simulator, mobile: setPermission writes the denial for its bundle. On a real iPhone the state is unreachable: only mobile: resetPermission runs there, and it clears.
solid answer
~40 sThree different answers, because the three targets have different powers. On **Android**, emulator or real device, `mobile: changePermissions` with a revoke action on the harvest-log package puts the camera permission into a denied state, and `mobile: getPermissions` reads it back so setup can assert its own work. On an **Apple Simulator**, `mobile: setPermission` writes the decision for the harvest-log bundle directly. On a **real Apple device** the state is simply not reachable: `mobile: setPermission` is Simulator-only, and `mobile: resetPermission` — the one command that runs there — only clears the decision so the prompt reappears. Seed while the app is backgrounded or not yet activated, because changing a permission a running app already holds can restart its process. What the case then asserts about the degraded flow is test-design work, not the driver's.
code
java · 13 linesif ("Android".equalsIgnoreCase(platform)) {
driver.executeScript("mobile: changePermissions", Map.of(
"action", "revoke",
"appPackage", "com.vineyard.harvestlog",
"permissions", "android.permission.CAMERA"));
} else if (isSimulator) {
driver.executeScript("mobile: setPermission", Map.of(
"bundleId", "com.vineyard.harvestlog",
"access", Map.of("camera", "no")));
} else {
throw new IllegalStateException(
"camera cannot be seeded denied on a real Apple device");
}go deeper
Know the two seeding commands by platform: revoke with mobile: changePermissions on Android, and write the decision with mobile: setPermission on an Apple Simulator. Say that a real Apple device cannot be seeded denied.
Explain the ordering as well as the commands: install first, seed while the app is not in the foreground, read the state back, then activate. Say why a revoke can restart a running process.
Show judgment about the unreachable case. Describe an explicit skip with a reason instead of a swallowed exception, and how you keep the reset-plus-prompt flow separate from the seed-and-proceed flow.
Decide where this asymmetry is recorded. Treat reachable permission states as a declared property of each target class, so suite planning sees the limit rather than discovering it in a red run.
## The state you are trying to reach The **vineyard harvest-log app** photographs every bin arriving at the press house. A pickers' tablet whose camera the operator refused should fall back to a typed bin note, and the suite wants that path exercised without a human answering a modal. So the setup step has to put the device into a specific state — camera decided, and decided against — before the case opens the bin screen. That is a mechanics problem with three distinct answers, one per target class, and pretending it has a single answer is where suites go wrong. ## Android: revoke, then verify On Android the state is fully reachable on both emulators and real devices. `mobile: changePermissions`, declared in `appium-android-driver` and therefore available under the UiAutomator2 and Espresso drivers alike, takes a revoke action, the harvest-log package name and the camera permission string, and reaches the device's package manager to flip the decision. Two habits make it reliable: - **Do not lean on `appium:autoGrantPermissions` here.** It is a blanket install-time grant with no exclusion list, so on a session that uses it your revoke is a correction applied afterwards, not an alternative to it. - **Read it back.** `mobile: getPermissions` lets the setup step assert that the revoke landed. Setup that verifies itself converts a mysterious mid-case modal into a clear failure at the top of the case. ## Apple Simulator: write the decision On a Simulator the XCUITest driver's `mobile: setPermission` writes the decision straight in: name the harvest-log bundle identifier, name the camera service, set it to the denied value. The command reaches the Simulator's privacy store through host-side tooling, which is precisely why it is confined to Simulators. Its reader `mobile: getPermission` is available on the same targets, so the verify habit carries across. ## Apple real device: the state is not reachable This is the answer an interviewer is listening for. On a plugged-in iPhone or iPad: - `mobile: setPermission` throws — it is Simulator-only, so there is no direct route to a denied state. - `mobile: getPermission` throws too, so you cannot even inspect the current state. - `mobile: resetPermission` does work, proxying to WebDriverAgent's `/wda/resetAppAuth`, but it clears the decision. It gets you to a **prompt**, not to a denial. So a real-device run of this case is a materially different flow: reach the prompt and answer it, rather than seed the state and proceed. Treating that as a transparent fallback inside a helper is a mistake — the case now depends on modal handling, which is its own mechanism and its own subject. ## Ordering, and the process restart Seeding is not only about the right command; it is about the right moment. 1. Install or reset the app first, so an install-time grant cannot overwrite what you seed afterwards. 2. Seed while the app is **backgrounded or not yet activated**. Changing a permission an already-running app holds can take its process down with it, and a restart in the middle of a case is indistinguishable from a crash in the failure output. 3. Verify the state where the target lets you read it — Android always, an Apple Simulator always, an Apple real device never. 4. Activate the app and let the case begin from a known starting point. 5. Reset at teardown on shared devices so the next run does not inherit your answer. ## Comparing the three targets | Target | Command that seeds denied | Read-back available | |---|---|---| | Android emulator | `mobile: changePermissions`, revoke | `mobile: getPermissions` | | Android real device | `mobile: changePermissions`, revoke | `mobile: getPermissions` | | Apple Simulator | `mobile: setPermission` | `mobile: getPermission` | | Apple real device | none — only `mobile: resetPermission` clears | none | ## Where this stops being the driver's problem The seeding is mechanics. What the case then checks — that the bin screen offers the typed note, that nothing crashes, that the operator can recover — is test-design work owned elsewhere, and a good answer says so rather than drifting into it. Likewise, answering the prompt that a reset restores is modal handling, a neighbouring concern with its own commands. Keep the boundary crisp: this leaf gets the device into a state; other work decides which states are worth reaching and what to conclude once you are there. ## The failure mode to name out loud The worst outcome is a helper that tries `mobile: setPermission`, swallows the exception on real hardware, and lets the case run anyway. The case does not fail — it drifts. The app raises a prompt nobody expected, a later find times out on an obscured screen, and the report blames a locator. Fail or skip loudly with the state you could not reach, and the diagnosis is free.
- Why not let the helper swallow the error on a real Apple device and carry on?Because the case then runs against an unseeded state. The app raises a prompt nobody expected, the next find times out on an obscured screen, and the report blames a locator. Fail or skip with the state you could not reach named in the message, so the diagnosis costs nothing.
- Does it matter whether the app is running when you revoke a permission on Android?Yes. Changing a permission an already-running application holds can restart its process, which in a mid-case failure log looks exactly like a crash. Seed while the app is backgrounded or before you activate it, then bring it to the foreground so the case begins from a state you chose deliberately.
saying these in an interview costs you the question
- Uses one helper and assumes it works on every target
- Thinks appium:autoGrantPermissions false revokes a held permission
- Expects mobile: resetPermission to produce a denied state
- Seeds the permission with the app already in the foreground
- Never reads the permission state back before the case runs
- Swallows the real-device failure and lets the case continue