Update read wallpaper methods (U and above)

The following methods in WallpaperManager:
  - getDrawable()
  - getDrawable(int which)
  - peekDrawable()
  - peekDrawable(int which)
  - getFastDrawable()
  - getFastDrawable(int which)
  - peekFastDrawable()
  - peekFastDrawable(int which)
  - getWallpaperFile(int which)

Have been changed in T to return the default system wallpaper data unless the caller has the internal permission READ_WALLPAPER_INTERNAL (or MANAGE_EXTERNAL_STORAGE).

The READ_WALLPAPER_INTERNAL permission has been added in the SystemUI and shell AndroidManifest.

This CL also stops returning the cached wallpaper if an app doesn't hold
the READ_WALLPAPER_INTERNAL permission.

Test: treehugger
Test: atest CtsScopedStorageDeviceOnlyTest
Test: atest ScopedStorageHostTest
Test: atest CtsAppOpsTestCases
Test: atest CtsWallpaperTestCases
Test: atest NoWallpaperPermissionsTest
Test: atest LauncherIronwoodIntegrationTests (see
HomescreenCustomizationTests#testChangeWallpaper)
Bug: 261906287
Bug: 239784307

Change-Id: I8f90fbe78668e4e1d09188196180ca39c6b760b1
diff --git a/core/api/current.txt b/core/api/current.txt
index be36d63..872a274 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7549,23 +7549,23 @@
     method public android.content.Intent getCropAndSetWallpaperIntent(android.net.Uri);
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable(int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable(int);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable getDrawable();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable getDrawable(int);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable getFastDrawable();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable getFastDrawable(int);
     method public static android.app.WallpaperManager getInstance(android.content.Context);
     method @Nullable public android.app.WallpaperColors getWallpaperColors(int);
-    method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.os.ParcelFileDescriptor getWallpaperFile(int);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.os.ParcelFileDescriptor getWallpaperFile(int);
     method public int getWallpaperId(int);
     method public android.app.WallpaperInfo getWallpaperInfo();
     method @Nullable public android.app.WallpaperInfo getWallpaperInfo(int);
     method public boolean hasResourceWallpaper(@RawRes int);
     method public boolean isSetWallpaperAllowed();
     method public boolean isWallpaperSupported();
-    method @Nullable public android.graphics.drawable.Drawable peekDrawable();
-    method @Nullable public android.graphics.drawable.Drawable peekDrawable(int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable peekFastDrawable();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable peekFastDrawable(int);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable peekDrawable();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable peekDrawable(int);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable peekFastDrawable();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, "android.permission.READ_WALLPAPER_INTERNAL"}) public android.graphics.drawable.Drawable peekFastDrawable(int);
     method public void removeOnColorsChangedListener(@NonNull android.app.WallpaperManager.OnColorsChangedListener);
     method public void sendWallpaperCommand(android.os.IBinder, String, int, int, int, android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a9ae225..9dc54ba 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -473,7 +473,7 @@
     method @Nullable public android.graphics.Rect peekBitmapDimensions(int);
     method public void setWallpaperZoomOut(@NonNull android.os.IBinder, float);
     method public boolean shouldEnableWideColorGamut();
-    method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
+    method public boolean wallpaperSupportsWcg(int);
   }
 
   public class WindowConfiguration implements java.lang.Comparable<android.app.WindowConfiguration> android.os.Parcelable {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index ff17824..540342b 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,11 @@
 
 package android.app;
 
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -28,6 +33,9 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UiContext;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -84,6 +92,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -108,8 +117,26 @@
  */
 @SystemService(Context.WALLPAPER_SERVICE)
 public class WallpaperManager {
+
     private static String TAG = "WallpaperManager";
     private static final boolean DEBUG = false;
+
+    /**
+     * Trying to read the wallpaper file or bitmap in T will return
+     * the default wallpaper bitmap/file instead of throwing a SecurityException.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    static final long RETURN_DEFAULT_ON_SECURITY_EXCEPTION = 239784307L;
+
+    /**
+     * In U and later, attempting to read the wallpaper file or bitmap will throw an exception,
+     * (except with the READ_WALLPAPER_INTERNAL permission).
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    static final long THROW_ON_SECURITY_EXCEPTION = 237508058L;
+
     private float mWallpaperXStep = -1;
     private float mWallpaperYStep = -1;
     private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
@@ -585,7 +612,8 @@
                 }
             }
             synchronized (this) {
-                if (mCachedWallpaper != null && mCachedWallpaper.isValid(userId, which)) {
+                if (mCachedWallpaper != null && mCachedWallpaper.isValid(userId, which) && context
+                        .checkSelfPermission(READ_WALLPAPER_INTERNAL) == PERMISSION_GRANTED) {
                     return mCachedWallpaper.mCachedWallpaper;
                 }
                 mCachedWallpaper = null;
@@ -596,6 +624,19 @@
                 } catch (OutOfMemoryError e) {
                     Log.w(TAG, "Out of memory loading the current wallpaper: " + e);
                 } catch (SecurityException e) {
+                    /*
+                     * Apps with target SDK <= S can still access the wallpaper through
+                     * READ_EXTERNAL_STORAGE. In T however, app that previously had access to the
+                     * wallpaper via READ_EXTERNAL_STORAGE will get a SecurityException here.
+                     * Thus, in T specifically, return the default wallpaper instead of crashing.
+                     */
+                    if (CompatChanges.isChangeEnabled(RETURN_DEFAULT_ON_SECURITY_EXCEPTION)
+                            && !CompatChanges.isChangeEnabled(THROW_ON_SECURITY_EXCEPTION)) {
+                        Log.w(TAG, "No permission to access wallpaper, returning default"
+                                + " wallpaper to avoid crashing legacy app.");
+                        return getDefaultWallpaper(context, FLAG_SYSTEM);
+                    }
+
                     if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) {
                         Log.w(TAG, "No permission to access wallpaper, suppressing"
                                 + " exception to avoid crashing legacy app.");
@@ -808,6 +849,18 @@
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Retrieve the current system wallpaper; if
      * no wallpaper is set, the system built-in static wallpaper is returned.
      * This is returned as an
@@ -821,14 +874,28 @@
      * @return Returns a Drawable object that will draw the system wallpaper,
      *     or {@code null} if no system wallpaper exists or if the calling application
      *     is not able to access the wallpaper.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable getDrawable() {
         return getDrawable(FLAG_SYSTEM);
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Retrieve the requested wallpaper; if
      * no wallpaper is set, the requested built-in static wallpaper is returned.
      * This is returned as an
@@ -844,9 +911,11 @@
      * @return Returns a Drawable object that will draw the requested wallpaper,
      *     or {@code null} if the requested wallpaper does not exist or if the calling application
      *     is not able to access the wallpaper.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable getDrawable(@SetWallpaperFlags int which) {
         final ColorManagementProxy cmProxy = getColorManagementProxy();
         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, which, cmProxy);
@@ -1069,6 +1138,18 @@
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Retrieve the current system wallpaper; if there is no wallpaper set,
      * a null pointer is returned. This is returned as an
      * abstract Drawable that you can install in a View to display whatever
@@ -1076,13 +1157,28 @@
      *
      * @return Returns a Drawable object that will draw the wallpaper or a
      * null pointer if wallpaper is unset.
+     *
+     * @throws SecurityException as described in the note
      */
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable peekDrawable() {
         return peekDrawable(FLAG_SYSTEM);
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Retrieve the requested wallpaper; if there is no wallpaper set,
      * a null pointer is returned. This is returned as an
      * abstract Drawable that you can install in a View to display whatever
@@ -1092,11 +1188,14 @@
      *     IllegalArgumentException if an invalid wallpaper is requested.
      * @return Returns a Drawable object that will draw the wallpaper or a null pointer if
      * wallpaper is unset.
+     *
+     * @throws SecurityException as described in the note
      */
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable peekDrawable(@SetWallpaperFlags int which) {
         final ColorManagementProxy cmProxy = getColorManagementProxy();
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, which, cmProxy);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, which, cmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -1106,6 +1205,18 @@
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Like {@link #getDrawable()}, but the returned Drawable has a number
      * of limitations to reduce its overhead as much as possible. It will
      * never scale the wallpaper (only centering it if the requested bounds
@@ -1117,14 +1228,28 @@
      * the same density as the screen (not in density compatibility mode).
      *
      * @return Returns a Drawable object that will draw the wallpaper.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable getFastDrawable() {
         return getFastDrawable(FLAG_SYSTEM);
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Like {@link #getDrawable(int)}, but the returned Drawable has a number
      * of limitations to reduce its overhead as much as possible. It will
      * never scale the wallpaper (only centering it if the requested bounds
@@ -1138,9 +1263,11 @@
      * @param which The {@code FLAG_*} identifier of a valid wallpaper type.  Throws
      *     IllegalArgumentException if an invalid wallpaper is requested.
      * @return Returns a Drawable object that will draw the wallpaper.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable getFastDrawable(@SetWallpaperFlags int which) {
         final ColorManagementProxy cmProxy = getColorManagementProxy();
         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, which, cmProxy);
@@ -1151,19 +1278,45 @@
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
      * a null pointer is returned.
      *
      * @return Returns an optimized Drawable object that will draw the
      * wallpaper or a null pointer if these is none.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable peekFastDrawable() {
         return peekFastDrawable(FLAG_SYSTEM);
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
      * a null pointer is returned.
      *
@@ -1171,12 +1324,14 @@
      *     IllegalArgumentException if an invalid wallpaper is requested.
      * @return Returns an optimized Drawable object that will draw the
      * wallpaper or a null pointer if these is none.
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public Drawable peekFastDrawable(@SetWallpaperFlags int which) {
         final ColorManagementProxy cmProxy = getColorManagementProxy();
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, which, cmProxy);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, which, cmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -1194,7 +1349,6 @@
      * @hide
      */
     @TestApi
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public boolean wallpaperSupportsWcg(int which) {
         if (!shouldEnableWideColorGamut()) {
             return false;
@@ -1295,6 +1449,18 @@
     }
 
     /**
+     * <strong> Important note: </strong>
+     * <ul>
+     *     <li>Up to version S, this method requires the
+     *     {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
+     *     <li>Starting in T, directly accessing the wallpaper is not possible anymore,
+     *     instead the default system wallpaper is returned
+     *     (some versions of T may throw a {@code SecurityException}).</li>
+     *     <li>From version U, this method should not be used
+     *     and will always throw a @code SecurityException}.</li>
+     * </ul>
+     * <br>
+     *
      * Get an open, readable file descriptor to the given wallpaper image file.
      * The caller is responsible for closing the file descriptor when done ingesting the file.
      *
@@ -1305,14 +1471,17 @@
      * @param which The wallpaper whose image file is to be retrieved.  Must be a single
      *     defined kind of wallpaper, either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}.
-     * @return An open, readable file desriptor to the requested wallpaper image file;
+     * @return An open, readable file descriptor to the requested wallpaper image file;
      *     or {@code null} if no such wallpaper is configured or if the calling app does
      *     not have permission to read the current wallpaper.
      *
      * @see #FLAG_LOCK
      * @see #FLAG_SYSTEM
+     *
+     * @throws SecurityException as described in the note
      */
-    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
+    @Nullable
+    @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL})
     public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which) {
         return getWallpaperFile(which, mContext.getUserId());
     }
@@ -1475,13 +1644,18 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (SecurityException e) {
+                if (CompatChanges.isChangeEnabled(RETURN_DEFAULT_ON_SECURITY_EXCEPTION)
+                        && !CompatChanges.isChangeEnabled(THROW_ON_SECURITY_EXCEPTION)) {
+                    Log.w(TAG, "No permission to access wallpaper, returning default"
+                            + " wallpaper file to avoid crashing legacy app.");
+                    return getDefaultSystemWallpaperFile();
+                }
                 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) {
                     Log.w(TAG, "No permission to access wallpaper, suppressing"
                             + " exception to avoid crashing legacy app.");
                     return null;
-                } else {
-                    throw e;
                 }
+                throw e;
             }
         }
     }
@@ -2586,6 +2760,24 @@
         return null;
     }
 
+    /**
+     * util used in T to return a default system wallpaper file
+     * when third party apps attempt to read the wallpaper with {@link #getWallpaperFile}
+     */
+    private static ParcelFileDescriptor getDefaultSystemWallpaperFile() {
+        for (String path: getDefaultSystemWallpaperPaths()) {
+            File file = new File(path);
+            if (file.exists()) {
+                try {
+                    return ParcelFileDescriptor.open(file, MODE_READ_ONLY);
+                } catch (FileNotFoundException e) {
+                    // continue; default wallpaper file not found on this path
+                }
+            }
+        }
+        return null;
+    }
+
     private static InputStream getWallpaperInputStream(String path) {
         if (!TextUtils.isEmpty(path)) {
             final File file = new File(path);
@@ -2600,6 +2792,14 @@
         return null;
     }
 
+    /**
+     * @return a list of paths to the system default wallpapers, in order of priority:
+     * if the file exists for the first path of this list, the first path should be used.
+     */
+    private static List<String> getDefaultSystemWallpaperPaths() {
+        return List.of(SystemProperties.get(PROP_WALLPAPER), getCmfWallpaperPath());
+    }
+
     private static String getCmfWallpaperPath() {
         return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_"
                 + VALUE_CMF_COLOR;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cb6c092..736d71a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4036,8 +4036,7 @@
         android:description="@string/permdesc_setWallpaperHints"
         android:protectionLevel="normal" />
 
-    <!-- Allow the app to read the system wallpaper image without
-        holding the READ_EXTERNAL_STORAGE permission.
+    <!-- Allow the app to read the system and lock wallpaper images.
         <p>Not for use by third-party applications.
         @hide
         @SystemApi
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 116aa489..0e28043 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -51,6 +51,7 @@
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+        <permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 71f438e..cf6f495 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -27,6 +27,7 @@
 
     <!-- Used to read wallpaper -->
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
 
     <!-- Used to read storage for all users -->
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c9eef38..55060a6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -90,7 +90,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
@@ -2210,12 +2209,7 @@
     public ParcelFileDescriptor getWallpaperWithFeature(String callingPkg, String callingFeatureId,
             IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId,
             boolean getCropped) {
-        final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
-        if (!hasPrivilege) {
-            mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
-                    Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
-        }
-
+        checkPermission(READ_WALLPAPER_INTERNAL);
         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);