Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 197fe1f9 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Final push to build against SDK.

The bulk of the work needed to get MediaProvider building against
the "system_current" SDK surface has been slowly merged over the
last few months, and this change makes the last few adjustments.

This adds a new StorageVolumeCallback which is simpler version of
StorageEventListener that simply delivers the changed StorageVolume.

Move DownloadManager logic into a onMediaStoreDownloadsDeleted()
method which hides the implementation details of how the OS connects
with that implementation.

Make local copies of some ExifInterface parsing logic; they could
be added to the androidx version in an unbundled release.  Make a
local copy of RedactingFileDescriptor, since it's only needed for
the next few weeks until FUSE is globally enabled.

Bug: 137890034
Test: atest --test-mapping packages/providers/MediaProvider
Change-Id: Ib416eb8724781bdd234c8b7d728dee8b695ad6ac
parent cb8823d9
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -9502,6 +9502,7 @@ package android.content {
    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
    method @Nullable public final String getCallingFeatureId();
    method @Nullable public final String getCallingPackage();
    method @Nullable public final String getCallingPackageUnchecked();
    method @Nullable public final android.content.Context getContext();
    method @Nullable public final android.content.pm.PathPermission[] getPathPermissions();
    method @Nullable public final String getReadPermission();
@@ -9511,6 +9512,7 @@ package android.content {
    method @Nullable public abstract android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues);
    method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
    method protected boolean isTemporary();
    method public void onCallingPackageChanged();
    method public void onConfigurationChanged(android.content.res.Configuration);
    method public abstract boolean onCreate();
    method public void onLowMemory();
@@ -13321,6 +13323,7 @@ package android.database.sqlite {
    method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
    method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
    method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
    method @Nullable public java.util.Collection<java.util.regex.Pattern> getProjectionGreylist();
    method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
    method @Nullable public String getTables();
    method public long insert(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues);
@@ -13333,6 +13336,7 @@ package android.database.sqlite {
    method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
    method public void setCursorFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
    method public void setDistinct(boolean);
    method public void setProjectionGreylist(@Nullable java.util.Collection<java.util.regex.Pattern>);
    method public void setProjectionMap(@Nullable java.util.Map<java.lang.String,java.lang.String>);
    method public void setStrict(boolean);
    method public void setStrictColumns(boolean);
@@ -36344,15 +36348,22 @@ package android.os.storage {
    method public boolean isObbMounted(String);
    method public boolean mountObb(String, String, android.os.storage.OnObbStateChangeListener);
    method @NonNull public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException;
    method public void registerStorageVolumeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.storage.StorageManager.StorageVolumeCallback);
    method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException;
    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
    method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener);
    method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback);
    field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
    field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
    field public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
    field public static final java.util.UUID UUID_DEFAULT;
  }
  public static class StorageManager.StorageVolumeCallback {
    ctor public StorageManager.StorageVolumeCallback();
    method public void onStateChanged(@NonNull android.os.storage.StorageVolume);
  }
  public final class StorageVolume implements android.os.Parcelable {
    method @Deprecated @Nullable public android.content.Intent createAccessIntent(String);
    method @NonNull public android.content.Intent createOpenDocumentTreeIntent();
+15 −0
Original line number Diff line number Diff line
@@ -553,6 +553,7 @@ package android.app {
  }
  public class DownloadManager {
    method @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public void onMediaStoreDownloadsDeleted(@NonNull android.util.LongSparseArray<java.lang.String>);
    field public static final String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED";
  }
@@ -1629,11 +1630,17 @@ package android.content {
    method @NonNull public final android.os.UserHandle getSendingUser();
  }
  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
    method public int checkUriPermission(@NonNull android.net.Uri, int, int);
  }
  public class ContentProviderClient implements java.lang.AutoCloseable {
    method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
  }
  public abstract class ContentResolver {
    method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File);
    method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri);
    method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri);
    method @RequiresPermission("android.permission.CACHE_CONTENT") public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle);
  }
@@ -6873,6 +6880,10 @@ package android.os {
    method public boolean hasSingleFileDescriptor();
  }
  public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
    method @NonNull public static android.os.ParcelFileDescriptor wrap(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.Handler, @NonNull android.os.ParcelFileDescriptor.OnCloseListener) throws java.io.IOException;
  }
  public final class PowerManager {
    method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
@@ -7249,6 +7260,10 @@ package android.os.storage {
    field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
  }
  public final class StorageVolume implements android.os.Parcelable {
    method @NonNull public String getId();
  }
}
package android.permission {
+35 −0
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package android.app;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -40,11 +42,13 @@ import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.provider.BaseColumns;
import android.provider.Downloads;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Pair;

import java.io.File;
@@ -1068,6 +1072,37 @@ public class DownloadManager {
        mAccessFilename = accessFilename;
    }

    /**
     * Notify {@link DownloadManager} that the given {@link MediaStore} items
     * were just deleted so that {@link DownloadManager} internal data
     * structures can be cleaned up.
     *
     * @param idToMime map from {@link BaseColumns#_ID} to
     *            {@link ContentResolver#getType(Uri)}.
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
    public void onMediaStoreDownloadsDeleted(@NonNull LongSparseArray<String> idToMime) {
        try (ContentProviderClient client = mResolver
                .acquireUnstableContentProviderClient(mBaseUri)) {
           final Bundle callExtras = new Bundle();
           final long[] ids = new long[idToMime.size()];
           final String[] mimeTypes = new String[idToMime.size()];
           for (int i = idToMime.size() - 1; i >= 0; --i) {
               ids[i] = idToMime.keyAt(i);
               mimeTypes[i] = idToMime.valueAt(i);
           }
           callExtras.putLongArray(android.provider.Downloads.EXTRA_IDS, ids);
           callExtras.putStringArray(android.provider.Downloads.EXTRA_MIME_TYPES,
                   mimeTypes);
           client.call(android.provider.Downloads.CALL_MEDIASTORE_DOWNLOADS_DELETED,
                   null, callExtras);
        } catch (RemoteException e) {
            // Should not happen
        }
    }

    /**
     * Enqueue a new download.  The download will start automatically once the download manager is
     * ready to execute it and connectivity is available.
+38 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.os.Trace.TRACE_TAG_DATABASE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.PackageManager;
@@ -942,7 +943,18 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
        return null;
    }

    /** {@hide} */
    /**
     * Return the package name of the caller that initiated the request being
     * processed on the current thread. The returned package will have
     * <em>not</em> been verified to belong to the calling UID. Returns
     * {@code null} if not currently processing a request.
     * <p>
     * This will always return {@code null} when processing
     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
     *
     * @see Binder#getCallingUid()
     * @see Context#grantUriPermission(String, Uri, int)
     */
    public final @Nullable String getCallingPackageUnchecked() {
        final Pair<String, String> pkg = mCallingPackage.get();
        if (pkg != null) {
@@ -952,7 +964,14 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
        return null;
    }

    /** {@hide} */
    /**
     * Called whenever the value of {@link #getCallingPackage()} changes, giving
     * the provider an opportunity to invalidate any security related caching it
     * may be performing.
     * <p>
     * This typically happens when a {@link ContentProvider} makes a nested call
     * back into itself when already processing a call from a remote process.
     */
    public void onCallingPackageChanged() {
    }

@@ -1525,8 +1544,24 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
        return false;
    }

    /** {@hide} */
    /**
     * Perform a detailed internal check on a {@link Uri} to determine if a UID
     * is able to access it with specific mode flags.
     * <p>
     * This method is typically used when the provider implements more dynamic
     * access controls that cannot be expressed with {@code <path-permission>}
     * style static rules.
     *
     * @param uri the {@link Uri} to perform an access check on.
     * @param uid the UID to check the permission for.
     * @param modeFlags the access flags to use for the access check, such as
     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}.
     * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed,
     *         otherwise {@link PackageManager#PERMISSION_DENIED}.
     * @hide
     */
    @Override
    @SystemApi
    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) {
        return PackageManager.PERMISSION_DENIED;
    }
+34 −2
Original line number Diff line number Diff line
@@ -3851,15 +3851,47 @@ public abstract class ContentResolver implements ContentInterface {
        }
    }

    /**
     * Decode a path generated by {@link #encodeToFile(Uri)} back into
     * the original {@link Uri}.
     * <p>
     * This is used to offer a way to intercept filesystem calls in
     * {@link ContentProvider} unaware code and redirect them to a
     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
     * that are otherwise deprecated.
     *
     * @hide
     */
    @SystemApi
    public static @NonNull Uri decodeFromFile(@NonNull File file) {
        return translateDeprecatedDataPath(file.getAbsolutePath());
    }

    /**
     * Encode a {@link Uri} into an opaque filesystem path which can then be
     * resurrected by {@link #decodeFromFile(File)}.
     * <p>
     * This is used to offer a way to intercept filesystem calls in
     * {@link ContentProvider} unaware code and redirect them to a
     * {@link ContentProvider} when they attempt to use {@code _DATA} columns
     * that are otherwise deprecated.
     *
     * @hide
     */
    @SystemApi
    public static @NonNull File encodeToFile(@NonNull Uri uri) {
        return new File(translateDeprecatedDataPath(uri));
    }

    /** {@hide} */
    public static Uri translateDeprecatedDataPath(String path) {
    public static @NonNull Uri translateDeprecatedDataPath(@NonNull String path) {
        final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
        return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
                .encodedOpaquePart(ssp).build().toString());
    }

    /** {@hide} */
    public static String translateDeprecatedDataPath(Uri uri) {
    public static @NonNull String translateDeprecatedDataPath(@NonNull Uri uri) {
        return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
    }
}
Loading