( device: DeviceInfo, appPackage: string, pmAction: 'grant' | 'revoke', )
| 321 | } |
| 322 | |
| 323 | async function setAndroidPhotoPermission( |
| 324 | device: DeviceInfo, |
| 325 | appPackage: string, |
| 326 | pmAction: 'grant' | 'revoke', |
| 327 | ): Promise<void> { |
| 328 | const sdkInt = await getAndroidSdkInt(device); |
| 329 | const candidates = |
| 330 | sdkInt !== null && sdkInt >= 33 |
| 331 | ? ['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_EXTERNAL_STORAGE'] |
| 332 | : ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.READ_MEDIA_IMAGES']; |
| 333 | |
| 334 | const failures: Array<{ permission: string; stderr: string; exitCode: number }> = []; |
| 335 | for (const permission of candidates) { |
| 336 | const result = await runAndroidAdb(device, ['shell', 'pm', pmAction, appPackage, permission], { |
| 337 | allowFailure: true, |
| 338 | }); |
| 339 | if (result.exitCode === 0) return; |
| 340 | failures.push({ permission, stderr: result.stderr, exitCode: result.exitCode }); |
| 341 | } |
| 342 | |
| 343 | throw new AppError('COMMAND_FAILED', `Failed to ${pmAction} Android photos permission`, { |
| 344 | appPackage, |
| 345 | sdkInt, |
| 346 | attempts: failures, |
| 347 | }); |
| 348 | } |
| 349 | |
| 350 | async function setAndroidNotificationPermission( |
| 351 | device: DeviceInfo, |
no test coverage detected