skip to content

Your Appium vineyard harvest-log cases need camera access already denied — how do you seed that on Android and iOS?

level: seniorimportance: should knowfreq 44%

answer

  1. three targets, three different answers
  2. revoke on one, write on the other
  3. one target cannot reach it at all
  4. seed before the app is foregrounded

basics

~20 s

On 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 s

Three 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 lines
java
if ("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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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