Added a new api ContentProvider#getTypeAnonymous

- When an app is unable to acquire IContentProvider in contentResolver#getType,
they can only call getTypeAnonymous via activityManagerService.
- default getTypeUnchecked returns getType (to ensure less breaking)
- getType calls are protected by readPermission.
- updated logging of getType calls without readPermisison.

Bug: b/161370118

Test: Build, manual test and logging
Change-Id: Ia5fe69061de36ed70d93b30754d68b63ad791c83
diff --git a/core/api/current.txt b/core/api/current.txt
index eef16be..794d2c6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9848,6 +9848,7 @@
     method @Nullable public final String getReadPermission();
     method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String);
     method @Nullable public abstract String getType(@NonNull android.net.Uri);
+    method @Nullable public String getTypeAnonymous(@NonNull android.net.Uri);
     method @Nullable public final String getWritePermission();
     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);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index aa5b866..f653e13 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -347,7 +347,7 @@
     String getProviderMimeType(in Uri uri, int userId);
 
     oneway void getProviderMimeTypeAsync(in Uri uri, int userId, in RemoteCallback resultCallback);
-
+    oneway void getMimeTypeFilterAsync(in Uri uri, int userId, in RemoteCallback resultCallback);
     // Cause the specified process to dump the specified heap.
     boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
             boolean runGc, in String path, in ParcelFileDescriptor fd,
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e8f0a89..c8db0d8 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,14 +18,15 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myUserHandle;
 import static android.os.Trace.TRACE_TAG_DATABASE;
 
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION;
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_CHECK_URI_PERMISSION;
-import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_ERROR;
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_FRAMEWORK_PERMISSION;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -300,17 +301,32 @@
         }
 
         @Override
-        public String getType(Uri uri) {
+        public String getType(AttributionSource attributionSource, Uri uri) {
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
             try {
-                final String type = mInterface.getType(uri);
-                if (type != null) {
-                    logGetTypeData(Binder.getCallingUid(), uri, type);
+                if (checkGetTypePermission(attributionSource, uri)
+                        == PermissionChecker.PERMISSION_GRANTED) {
+                    final String type = mInterface.getType(uri);
+                    if (type != null) {
+                        logGetTypeData(Binder.getCallingUid(), uri, type, true);
+                    }
+                    return type;
+                } else {
+                    final int callingUid = Binder.getCallingUid();
+                    final long origId = Binder.clearCallingIdentity();
+                    try {
+                        final String type = getTypeAnonymous(uri);
+                        if (type != null) {
+                            logGetTypeData(callingUid, uri, type, false);
+                        }
+                        return type;
+                    } finally {
+                        Binder.restoreCallingIdentity(origId);
+                    }
                 }
-                return type;
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
@@ -319,59 +335,62 @@
         }
 
         // Utility function to log the getTypeData calls
-        private void logGetTypeData(int callingUid, Uri uri, String type) {
+        private void logGetTypeData(int callingUid, Uri uri, String type,
+                boolean permissionCheckPassed) {
             final int enumFrameworkPermission =
                     GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_FRAMEWORK_PERMISSION;
             final int enumCheckUriPermission =
                     GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_CHECK_URI_PERMISSION;
-            final int enumError = GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_ERROR;
-
-            try {
-                final AttributionSource attributionSource = new AttributionSource.Builder(
-                        callingUid).build();
-                try {
-                    if (enforceReadPermission(attributionSource, uri)
-                            != PermissionChecker.PERMISSION_GRANTED) {
-                        FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                                enumFrameworkPermission,
-                                callingUid, uri.getAuthority(), type);
-                    } else {
-                        final ProviderInfo cpi = mContext.getPackageManager()
-                                .resolveContentProvider(uri.getAuthority(),
+            if (permissionCheckPassed) {
+                // Just for logging for mediaProvider cases
+                final ProviderInfo cpi = mContext.getPackageManager()
+                        .resolveContentProvider(uri.getAuthority(),
                                 PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
-                        final int callingUserId = UserHandle.getUserId(callingUid);
-                        final Uri userUri = (mSingleUser
-                                && !UserHandle.isSameUser(mMyUid, callingUid))
-                                ? maybeAddUserId(uri, callingUserId) : uri;
-                        if (cpi.forceUriPermissions
-                                && mInterface.checkUriPermission(uri,
-                                callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                                != PermissionChecker.PERMISSION_GRANTED
-                                && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
-                                callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                                != PackageManager.PERMISSION_GRANTED) {
-                            FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                                    enumCheckUriPermission,
-                                    callingUid, uri.getAuthority(), type);
-                        }
+                final int callingUserId = UserHandle.getUserId(callingUid);
+                final Uri userUri = (mSingleUser
+                        && !UserHandle.isSameUser(mMyUid, callingUid))
+                        ? maybeAddUserId(uri, callingUserId) : uri;
+                try {
+                    if (cpi.forceUriPermissions
+                            && mInterface.checkUriPermission(uri,
+                            callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                            != PermissionChecker.PERMISSION_GRANTED
+                            && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
+                            callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                            != PackageManager.PERMISSION_GRANTED) {
+                        FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
+                                enumCheckUriPermission,
+                                callingUid, uri.getAuthority(), type);
                     }
-                } catch (SecurityException e) {
-                    FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                            enumFrameworkPermission,
-                            callingUid, uri.getAuthority(), type);
+                } catch (RemoteException e) {
+                    //does nothing
                 }
-            } catch (Exception e) {
+            } else {
                 FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                        enumError,
+                        enumFrameworkPermission,
                         callingUid, uri.getAuthority(), type);
             }
         }
 
         @Override
-        public void getTypeAsync(Uri uri, RemoteCallback callback) {
+        public void getTypeAsync(AttributionSource attributionSource,
+                Uri uri, RemoteCallback callback) {
             final Bundle result = new Bundle();
             try {
-                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT,
+                        getType(attributionSource, uri));
+            } catch (Exception e) {
+                result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
+                        new ParcelableException(e));
+            }
+            callback.sendResult(result);
+        }
+
+        @Override
+        public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) {
+            final Bundle result = new Bundle();
+            try {
+                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri));
             } catch (Exception e) {
                 result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
                         new ParcelableException(e));
@@ -795,6 +814,23 @@
             }
             return PermissionChecker.PERMISSION_GRANTED;
         }
+
+        @PermissionCheckerManager.PermissionResult
+        private int checkGetTypePermission(@NonNull AttributionSource attributionSource,
+                Uri uri) {
+            final int callingUid = Binder.getCallingUid();
+            if (UserHandle.getAppId(callingUid) == SYSTEM_UID
+                    || checkPermission(Manifest.permission.GET_ANY_PROVIDER_TYPE, attributionSource)
+                    == PermissionChecker.PERMISSION_GRANTED) {
+                // Allowing System Uid and apps with permission to get any type, to access all types
+                return PermissionChecker.PERMISSION_GRANTED;
+            }
+            try {
+                return enforceReadPermission(attributionSource, uri);
+            } catch (SecurityException e) {
+                return PermissionChecker.PERMISSION_HARD_DENIED;
+            }
+        }
     }
 
     boolean checkUser(int pid, int uid, Context context) {
@@ -1625,11 +1661,15 @@
      * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
      * and Threads</a>.
      *
-     * <p>Note that there are no permissions needed for an application to
+     * <p>Note that by default there are no permissions needed for an application to
      * access this information; if your content provider requires read and/or
      * write permissions, or is not exported, all applications can still call
-     * this method regardless of their access permissions.  This allows them
-     * to retrieve the MIME type for a URI when dispatching intents.
+     * this method regardless of their access permissions. </p>
+     *
+     * <p>If your mime type reveals details that should be protected,
+     * then you should protect this method by implementing {@link #getTypeAnonymous}.
+     * Implementing {@link #getTypeAnonymous} ensures your {@link #getType} can be
+     * only accessed by caller's having associated readPermission for the URI. </p>
      *
      * @param uri the URI to query.
      * @return a MIME type string, or {@code null} if there is no type.
@@ -1638,6 +1678,24 @@
     public abstract @Nullable String getType(@NonNull Uri uri);
 
     /**
+     * Implement this to handle requests for MIME type of URIs, that does not need to
+     * reveal any internal information which should be protected by any permission.
+     *
+     * <p>If your mime type reveals details that should be protected, then you should protect those
+     * by implementing those in {@link #getType}, and in this function, only return types of
+     * URIs which can be obtained by anyone without any access.
+     *
+     * Implementing ths function will make sure {@link #getType} is protected by readPermission.
+     * This function by default works as the {@link #getType}</p>
+     *
+     * @param uri the URI to query.
+     * @return a MIME type string, or {@code null} if type needs to be protected.
+     */
+    public @Nullable String getTypeAnonymous(@NonNull Uri uri) {
+        return getType(uri);
+    }
+
+    /**
      * Implement this to support canonicalization of URIs that refer to your
      * content provider.  A canonical URI is one that can be transported across
      * devices, backup/restore, and other contexts, and still be able to refer
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 47c96699..4ba3ff4 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -140,8 +140,10 @@
                 case GET_TYPE_TRANSACTION:
                 {
                     data.enforceInterface(IContentProvider.descriptor);
+                    AttributionSource attributionSource = AttributionSource.CREATOR
+                            .createFromParcel(data);
                     Uri url = Uri.CREATOR.createFromParcel(data);
-                    String type = getType(url);
+                    String type = getType(attributionSource, url);
                     reply.writeNoException();
                     reply.writeString(type);
 
@@ -150,9 +152,19 @@
 
                 case GET_TYPE_ASYNC_TRANSACTION: {
                     data.enforceInterface(IContentProvider.descriptor);
+                    AttributionSource attributionSource = AttributionSource.CREATOR
+                            .createFromParcel(data);
                     Uri url = Uri.CREATOR.createFromParcel(data);
                     RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
-                    getTypeAsync(url, callback);
+                    getTypeAsync(attributionSource, url, callback);
+                    return true;
+                }
+
+                case GET_TYPE_ANONYMOUS_ASYNC_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+                    getTypeAnonymousAsync(url, callback);
                     return true;
                 }
 
@@ -502,13 +514,13 @@
     }
 
     @Override
-    public String getType(Uri url) throws RemoteException
+    public String getType(AttributionSource attributionSource, Uri url) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         try {
             data.writeInterfaceToken(IContentProvider.descriptor);
-
+            attributionSource.writeToParcel(data, 0);
             url.writeToParcel(data, 0);
 
             mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0);
@@ -523,7 +535,25 @@
     }
 
     @Override
-    /* oneway */ public void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+    /* oneway */ public void getTypeAsync(AttributionSource attributionSource,
+            Uri uri, RemoteCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+            attributionSource.writeToParcel(data, 0);
+            uri.writeToParcel(data, 0);
+            callback.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.GET_TYPE_ASYNC_TRANSACTION, data, null,
+                    IBinder.FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @Override
+    /* oneway */ public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         try {
             data.writeInterfaceToken(IContentProvider.descriptor);
@@ -531,7 +561,7 @@
             uri.writeToParcel(data, 0);
             callback.writeToParcel(data, 0);
 
-            mRemote.transact(IContentProvider.GET_TYPE_ASYNC_TRANSACTION, data, null,
+            mRemote.transact(IContentProvider.GET_TYPE_ANONYMOUS_ASYNC_TRANSACTION, data, null,
                     IBinder.FLAG_ONEWAY);
         } finally {
             data.recycle();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 3779453..b84eb11 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -920,8 +920,13 @@
             return null;
         }
 
-        // XXX would like to have an acquireExistingUnstableProvider for this.
-        IContentProvider provider = acquireExistingProvider(url);
+        IContentProvider provider = null;
+        try {
+            provider = acquireProvider(url);
+        } catch (Exception e) {
+            // if unable to acquire the provider, then it should try to get the type
+            // using getTypeAnonymous via ActivityManagerService
+        }
         if (provider != null) {
             try {
                 final StringResultListener resultListener = new StringResultListener();
@@ -949,7 +954,7 @@
 
         try {
             final StringResultListener resultListener = new StringResultListener();
-            ActivityManager.getService().getProviderMimeTypeAsync(
+            ActivityManager.getService().getMimeTypeFilterAsync(
                     ContentProvider.getUriWithoutUserId(url),
                     resolveUserId(url),
                     new RemoteCallback(resultListener));
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index e0315a3..eb80148 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
@@ -44,14 +45,42 @@
             @Nullable String[] projection,
             @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
             throws RemoteException;
-    String getType(Uri url) throws RemoteException;
+    /**
+     * getType function with AttributionSource
+     */
+    String getType(@NonNull AttributionSource attributionSource,
+            Uri url) throws RemoteException;
+    /**
+     * one way getType function with AttributionSource
+     */
+    void getTypeAsync(@NonNull AttributionSource attributionSource,
+            Uri url, RemoteCallback callback) throws RemoteException;
+    /**
+     * @deprecated -- use getType with AttributionSource
+     */
+    @Deprecated
+    default String getType(Uri url) throws RemoteException {
+        return getType(new AttributionSource(Binder.getCallingUid(),
+                AppGlobals.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0],
+                null), url);
+    }
 
     /**
      * A oneway version of getType. The functionality is exactly the same, except that the
      * call returns immediately, and the resulting type is returned when available via
      * a binder callback.
+     *
+     * @deprecated -- use getTypeAsync with AttributionSource
      */
-    void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException;
+    @Deprecated
+    default void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+        getTypeAsync(new AttributionSource(Binder.getCallingUid(),
+                AppGlobals.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0],
+                null), uri, callback);
+    }
+
+    /** oneway version of getTypeAnonymous*/
+    void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) throws RemoteException;
 
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
@@ -185,4 +214,7 @@
     int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
     int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
     int UNCANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 30;
+    int GET_TYPE_ANONYMOUS_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 31;
+
+
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e9771a9..e243122 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7604,6 +7604,13 @@
     <permission android:name="android.permission.LOG_PROCESS_ACTIVITIES"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- @hide Allows an application to get type of any provider uri.
+         <p>Not for use by third-party applications.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.GET_ANY_PROVIDER_TYPE"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 96d5f38..6aa49d2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -191,7 +191,6 @@
 import android.app.ApplicationThreadConstants;
 import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
-import android.app.ComponentOptions;
 import android.app.ContentProviderHolder;
 import android.app.ForegroundServiceDelegationOptions;
 import android.app.IActivityController;
@@ -7031,6 +7030,17 @@
         mCpHelper.getProviderMimeTypeAsync(uri, userId, resultCallback);
     }
 
+    /**
+     * Filters calls to getType based on permission. If the caller has required permission,
+     * then it returns the contentProvider#getType.
+     * Else, it returns the contentProvider#getTypeAnonymous, which does not
+     * reveal any internal information which should be protected by any permission.
+     */
+    @Override
+    public void getMimeTypeFilterAsync(Uri uri, int userId, RemoteCallback resultCallback) {
+        mCpHelper.getMimeTypeFilterAsync(uri, userId, resultCallback);
+    }
+
     // =========================================================
     // GLOBAL MANAGEMENT
     // =========================================================
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 3130166..f721d69 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.am;
 
+import static android.Manifest.permission.GET_ANY_PROVIDER_TYPE;
 import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
 import static android.os.Process.PROC_CHAR;
 import static android.os.Process.PROC_OUT_LONG;
@@ -23,8 +24,6 @@
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION;
-import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_CHECK_URI_PERMISSION;
-import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_ERROR;
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION;
 import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
@@ -52,7 +51,6 @@
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.Intent;
-import android.content.PermissionChecker;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -1019,9 +1017,6 @@
                 mService.mHandler.postDelayed(providerNotResponding, 1000);
                 try {
                     final String type = holder.provider.getType(uri);
-                    if (type != null) {
-                        backgroundLogging(callingUid, callingPid, userId, uri, holder, type);
-                    }
                     return type;
                 } finally {
                     mService.mHandler.removeCallbacks(providerNotResponding);
@@ -1079,10 +1074,6 @@
                         Binder.restoreCallingIdentity(identity);
                     }
                     resultCallback.sendResult(result);
-                    final String type = result.getPairValue();
-                    if (type != null) {
-                        backgroundLogging(callingUid, callingPid, userId, uri, holder, type);
-                    }
                 }));
             } else {
                 resultCallback.sendResult(Bundle.EMPTY);
@@ -1093,65 +1084,82 @@
         }
     }
 
-    private void backgroundLogging(int callingUid, int callingPid, int userId, Uri uri,
-            ContentProviderHolder holder, String type) {
-        // Push the logging code in a different handlerThread.
+    /**
+     * Filters calls to getType based on permission. If the caller has required permission,
+     * then it returns the contentProvider#getType.
+     * Else, it returns the contentProvider#getTypeAnonymous, which does not
+     * reveal any internal information which should be protected by any permission.
+     */
+    void getMimeTypeFilterAsync(Uri uri, int userId, RemoteCallback resultCallback) {
+        mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync");
+        final String name = uri.getAuthority();
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+        final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
+                ? Binder.clearCallingIdentity() : 0;
+        final ContentProviderHolder holder;
         try {
-            mService.mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    logGetTypeData(callingUid, callingPid, userId,
-                            uri, holder, type);
+            holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid,
+                    "*getmimetype*", safeUserId);
+        } finally {
+            if (ident != 0) {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        try {
+            if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
+                if (checkGetAnyTypePermission(callingUid, callingPid)) {
+                    final AttributionSource attributionSource =
+                            new AttributionSource.Builder(callingUid).build();
+                    holder.provider.getTypeAsync(attributionSource,
+                            uri, new RemoteCallback(result -> {
+                                final long identity = Binder.clearCallingIdentity();
+                                try {
+                                    removeContentProviderExternalUnchecked(name, null, safeUserId);
+                                } finally {
+                                    Binder.restoreCallingIdentity(identity);
+                                }
+                                resultCallback.sendResult(result);
+                            }));
+                } else {
+                    holder.provider.getTypeAnonymousAsync(uri, new RemoteCallback(result -> {
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            removeContentProviderExternalUnchecked(name, null, safeUserId);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                        resultCallback.sendResult(result);
+                        final String type = result.getPairValue();
+                        if (type != null) {
+                            logGetTypeData(callingUid, uri, type);
+                        }
+                    }));
                 }
-            });
-        } catch (Exception e) {
-            // To ensure logging does not break the getType calls.
+            } else {
+                resultCallback.sendResult(Bundle.EMPTY);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Content provider dead retrieving " + uri, e);
+            resultCallback.sendResult(Bundle.EMPTY);
         }
     }
 
-    // Utility function to log the getTypeData calls
-    private void logGetTypeData(int callingUid, int callingPid, int userId, Uri uri,
-            ContentProviderHolder holder, String type) {
-        try {
-            boolean checkUser = true;
-            final String authority = uri.getAuthority();
-            if (isAuthorityRedirectedForCloneProfile(authority)) {
-                UserManagerInternal umInternal =
-                        LocalServices.getService(UserManagerInternal.class);
-                UserInfo userInfo = umInternal.getUserInfo(userId);
-
-                if (userInfo != null && userInfo.isCloneProfile()) {
-                    userId = umInternal.getProfileParentId(userId);
-                    checkUser = false;
-                }
-            }
-            final ProviderInfo cpi = holder.info;
-            final AttributionSource attributionSource =
-                    new AttributionSource.Builder(callingUid).build();
-            final String permissionCheck =
-                    checkContentProviderPermission(cpi, callingPid, callingUid,
-                            userId, checkUser, null);
-            final boolean grantCheck = mService.checkUriPermission(uri, callingPid, callingUid,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION , userId, null)
-                    == PackageManager.PERMISSION_GRANTED;
-
-            if (!grantCheck && permissionCheck != null) {
-                FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                        GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION,
-                        callingUid, authority, type);
-            } else if (!grantCheck && cpi.forceUriPermissions
-                    && holder.provider.checkUriPermission(attributionSource,
-                            uri, callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                            != PermissionChecker.PERMISSION_GRANTED) {
-                FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                        GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_CHECK_URI_PERMISSION,
-                        callingUid, authority, type);
-            }
-        } catch (Exception e) {
-            FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
-                    GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_ERROR, callingUid,
-                    uri.getAuthority(), type);
+    private boolean checkGetAnyTypePermission(int callingUid, int callingPid) {
+        if (mService.checkPermission(GET_ANY_PROVIDER_TYPE, callingPid, callingUid)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
         }
+        return false;
+    }
+
+    // Utility function to log the getTypeData calls
+    private void logGetTypeData(int callingUid, Uri uri, String type) {
+        FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
+                GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION,
+                callingUid, uri.getAuthority(), type);
     }
 
     private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 7be42f4..7f084f89 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -80,16 +80,22 @@
         }
 
         @Override
-        public String getType(Uri url) throws RemoteException {
+        public String getType(@NonNull AttributionSource attributionSource,
+                Uri url) throws RemoteException {
             return MockContentProvider.this.getType(url);
         }
 
         @Override
-        public void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+        public void getTypeAsync(@NonNull AttributionSource attributionSource,
+                Uri uri, RemoteCallback callback) throws RemoteException {
             MockContentProvider.this.getTypeAsync(uri, callback);
         }
 
         @Override
+        public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+            MockContentProvider.this.getTypeAnonymousAsync(uri, callback);
+        }
+        @Override
         public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
                 ContentValues initialValues, Bundle extras) throws RemoteException {
             return MockContentProvider.this.insert(url, initialValues, extras);
@@ -247,6 +253,23 @@
     }
 
     @Override
+    public String getTypeAnonymous(Uri uri) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    /**
+     * @hide
+     */
+    @SuppressWarnings("deprecation")
+    public void getTypeAnonymousAsync(Uri uri, RemoteCallback remoteCallback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            final Bundle bundle = new Bundle();
+            bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri));
+            remoteCallback.sendResult(bundle);
+        });
+    }
+
+    @Override
     public Uri insert(Uri uri, ContentValues values) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index b81c707..bb2996a 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -61,16 +61,30 @@
     }
 
     @Override
-    public String getType(Uri url) {
+    public String getType(@NonNull AttributionSource attributionSource, Uri url) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
     @Override
     @SuppressWarnings("deprecation")
-    public void getTypeAsync(Uri uri, RemoteCallback remoteCallback) {
+    public void getTypeAsync(@NonNull AttributionSource attributionSource,
+            Uri uri, RemoteCallback remoteCallback) {
         AsyncTask.SERIAL_EXECUTOR.execute(() -> {
             final Bundle bundle = new Bundle();
-            bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(attributionSource,
+                    uri));
+            remoteCallback.sendResult(bundle);
+        });
+    }
+    public String getTypeAnonymous(Uri url) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+    @Override
+    @SuppressWarnings("deprecation")
+    public void getTypeAnonymousAsync(Uri uri, RemoteCallback remoteCallback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            final Bundle bundle = new Bundle();
+            bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri));
             remoteCallback.sendResult(bundle);
         });
     }