2018年3月30日金曜日

Wallpaper の取得に permission が必要になっていたのでコードの変遷を調べてみた

昔は WallpaperManager の getDrawable() では READ_EXTERNAL_STORAGE permission が必要なかったのですが、targetSdkVersion をあげたところ必要だと怒られてしまったので、コードの変遷を追ってみました。

以下のコードを試します。 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val d = WallpaperManager.getInstance(this).drawable } }

targetSdkVersion = 27

targetSdkVersion = 27 で実行すると、READ_EXTERNAL_STORAGE permission が無いと怒られました。

Caused by: java.lang.SecurityException: read wallpaper: Neither user 10278 nor current process has android.permission.READ_EXTERNAL_STORAGE.

targetSdkVersion = 26

targetSdkVersion = 26 (compileSdkVersion は 27)で実行すると怒られませんでした。代わりに Warning がログに出ます。

W/WallpaperManager: No permission to access wallpaper, suppressing exception to avoid crashing legacy app.

android-27 での変更

26と27の間でコードの変更がありました。

android-26 public Drawable getDrawable() { Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM); if (bm != null) { Drawable dr = new BitmapDrawable(mContext.getResources(), bm); dr.setDither(false); return dr; } return null; } static class Globals extends IWallpaperManagerCallback.Stub { ... public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId) { ... synchronized (this) { if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) { return mCachedWallpaper; } mCachedWallpaper = null; mCachedWallpaperUserId = 0; try { mCachedWallpaper = getCurrentWallpaperLocked(userId); mCachedWallpaperUserId = userId; } catch (OutOfMemoryError e) { Log.w(TAG, "No memory load current wallpaper", e); } if (mCachedWallpaper != null) { return mCachedWallpaper; } } ... } android-27 public Drawable getDrawable() { Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM); if (bm != null) { Drawable dr = new BitmapDrawable(mContext.getResources(), bm); dr.setDither(false); return dr; } return null; } private static class Globals extends IWallpaperManagerCallback.Stub { ... public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId) { ... synchronized (this) { ... try { mCachedWallpaper = getCurrentWallpaperLocked(context, userId); mCachedWallpaperUserId = userId; } catch (OutOfMemoryError e) { Log.w(TAG, "Out of memory loading the current wallpaper: " + e); } catch (SecurityException e) { if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) { Log.w(TAG, "No permission to access wallpaper, suppressing" + " exception to avoid crashing legacy app."); } else { // Post-O apps really most sincerely need the permission. throw e; } } if (mCachedWallpaper != null) { return mCachedWallpaper; } } ... } android-26 のコードに比べて catch (SecurityException e) { ... } の部分↓が増えていました。 targetSdkVersion が27以降なら SecurityException をそのまま流すように変わったということです。 } catch (SecurityException e) { if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) { Log.w(TAG, "No permission to access wallpaper, suppressing" + " exception to avoid crashing legacy app."); } else { // Post-O apps really most sincerely need the permission. throw e; } } さらに、getCurrentWallpaperLocked() の引数に context が増えています。

android-26 private Bitmap getCurrentWallpaperLocked(int userId) { ... try { Bundle params = new Bundle(); ParcelFileDescriptor fd = mService.getWallpaper(this, FLAG_SYSTEM, params, userId); ... } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return null; } android-27 private Bitmap getCurrentWallpaperLocked(Context context, int userId) { ... try { Bundle params = new Bundle(); ParcelFileDescriptor fd = mService.getWallpaper(context.getOpPackageName(), this, FLAG_SYSTEM, params, userId); ... } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return null; } IWallpaperManager.getWallpaper() にも新しい引数が増えており、この context を使ってその引数に context.getOpPackageName() を渡しています。


ちなみに 27 (Android 8.1)では WallpaperColors API が追加されています。

結論

Wallpaper の Drawable を取得するには、READ_EXTERNAL_STORAGE permission を Manifest に追加して、Runtime Permission 処理を書く必要があります。


0 件のコメント:

コメントを投稿