Merge "Added a new api ContentProvider#getTypeAnonymous" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3d907cca3c
@@ -9848,6 +9848,7 @@ package android.content {
|
||||
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);
|
||||
|
||||
@@ -347,7 +347,7 @@ interface IActivityManager {
|
||||
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,
|
||||
|
||||
@@ -18,14 +18,15 @@ package android.content;
|
||||
|
||||
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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
||||
}
|
||||
|
||||
@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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
||||
}
|
||||
|
||||
// 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
||||
}
|
||||
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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
||||
* <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.
|
||||
@@ -1637,6 +1677,24 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
||||
@Override
|
||||
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
|
||||
|
||||
@@ -140,8 +140,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
|
||||
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 @@ final class ContentProviderProxy implements IContentProvider
|
||||
}
|
||||
|
||||
@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 @@ final class ContentProviderProxy implements IContentProvider
|
||||
}
|
||||
|
||||
@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 @@ final class ContentProviderProxy implements IContentProvider
|
||||
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();
|
||||
|
||||
@@ -920,8 +920,13 @@ public abstract class ContentResolver implements ContentInterface {
|
||||
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 @@ public abstract class ContentResolver implements ContentInterface {
|
||||
|
||||
try {
|
||||
final StringResultListener resultListener = new StringResultListener();
|
||||
ActivityManager.getService().getProviderMimeTypeAsync(
|
||||
ActivityManager.getService().getMimeTypeFilterAsync(
|
||||
ContentProvider.getUriWithoutUserId(url),
|
||||
resolveUserId(url),
|
||||
new RemoteCallback(resultListener));
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.content;
|
||||
|
||||
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 @@ public interface IContentProvider extends IInterface {
|
||||
@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 @@ public interface IContentProvider extends IInterface {
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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. -->
|
||||
|
||||
@@ -191,7 +191,6 @@ import android.app.ApplicationStartInfo;
|
||||
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 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
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
|
||||
// =========================================================
|
||||
|
||||
@@ -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.PROC_SPACE_TERM;
|
||||
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.ContentResolver;
|
||||
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 @@ public class ContentProviderHelper {
|
||||
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 @@ public class ContentProviderHelper {
|
||||
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 @@ public class ContentProviderHelper {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -80,15 +80,21 @@ public class MockContentProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
@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 {
|
||||
@@ -246,6 +252,23 @@ public class MockContentProvider extends ContentProvider {
|
||||
});
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
@@ -61,16 +61,30 @@ public class MockIContentProvider implements IContentProvider {
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user