2018年3月30日金曜日

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

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

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

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
  1. public Drawable getDrawable() {  
  2.     Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM);  
  3.     if (bm != null) {  
  4.         Drawable dr = new BitmapDrawable(mContext.getResources(), bm);  
  5.         dr.setDither(false);  
  6.         return dr;  
  7.     }  
  8.     return null;  
  9. }  
  10.   
  11. static class Globals extends IWallpaperManagerCallback.Stub {  
  12.     ...  
  13.   
  14.     public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,  
  15.             @SetWallpaperFlags int which, int userId) {  
  16.         ...  
  17.         synchronized (this) {  
  18.             if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {  
  19.                 return mCachedWallpaper;  
  20.             }  
  21.             mCachedWallpaper = null;  
  22.             mCachedWallpaperUserId = 0;  
  23.             try {  
  24.                 mCachedWallpaper = getCurrentWallpaperLocked(userId);  
  25.                 mCachedWallpaperUserId = userId;  
  26.             } catch (OutOfMemoryError e) {  
  27.                 Log.w(TAG, "No memory load current wallpaper", e);  
  28.             }  
  29.             if (mCachedWallpaper != null) {  
  30.                 return mCachedWallpaper;  
  31.             }  
  32.         }  
  33.         ...  
  34.     }  
android-27
  1. public Drawable getDrawable() {  
  2.     Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM);  
  3.     if (bm != null) {  
  4.         Drawable dr = new BitmapDrawable(mContext.getResources(), bm);  
  5.         dr.setDither(false);  
  6.         return dr;  
  7.     }  
  8.     return null;  
  9. }  
  10.   
  11. private static class Globals extends IWallpaperManagerCallback.Stub {  
  12.     ...  
  13.   
  14.     public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,  
  15.             @SetWallpaperFlags int which, int userId) {  
  16.         ...  
  17.         synchronized (this) {  
  18.             ...  
  19.             try {  
  20.                 mCachedWallpaper = getCurrentWallpaperLocked(context, userId);  
  21.                 mCachedWallpaperUserId = userId;  
  22.             } catch (OutOfMemoryError e) {  
  23.                 Log.w(TAG, "Out of memory loading the current wallpaper: " + e);  
  24.             } catch (SecurityException e) {  
  25.                 if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) {  
  26.                     Log.w(TAG, "No permission to access wallpaper, suppressing"  
  27.                             + " exception to avoid crashing legacy app.");  
  28.                 } else {  
  29.                     // Post-O apps really most sincerely need the permission.  
  30.                     throw e;  
  31.                 }  
  32.             }  
  33.             if (mCachedWallpaper != null) {  
  34.                 return mCachedWallpaper;  
  35.             }  
  36.         }  
  37.         ...  
  38.     }  
android-26 のコードに比べて catch (SecurityException e) { ... } の部分↓が増えていました。 targetSdkVersion が27以降なら SecurityException をそのまま流すように変わったということです。
  1. catch (SecurityException e) {  
  2.     if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) {  
  3.         Log.w(TAG, "No permission to access wallpaper, suppressing"  
  4.                 + " exception to avoid crashing legacy app.");  
  5.     } else {  
  6.         // Post-O apps really most sincerely need the permission.  
  7.         throw e;  
  8.     }  
  9. }  
さらに、getCurrentWallpaperLocked() の引数に context が増えています。

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


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

結論

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


0 件のコメント:

コメントを投稿