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

Commit a0f1107f authored by Himanshu Dagar's avatar Himanshu Dagar
Browse files

Media CP queries redirection from clone profile to its parent

As of now when a clone profile user [android.os.usertype.profile.CLONE] queries any content provider (e.g. media, ...etc), it gets served by the corresponding content provider running in that user space. So, in case of the media content provider, sharing of media items of the owner user space is not possible in the clone profile user space. To facilitate the same, media content provider queries are getting redirected to the owner user space's content provider as a part of this CL.

Bug: 202035750
Test: atest CtsAppCloningHostTest
Change-Id: I09500f86dbd149739422695b6dbd9f1a9717b4e6
parent 5cc91acd
Loading
Loading
Loading
Loading
+50 −4
Original line number Diff line number Diff line
@@ -48,10 +48,13 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.PermissionCheckerManager;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseBooleanArray;

import com.android.internal.annotations.VisibleForTesting;

@@ -134,9 +137,18 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
    private boolean mExported;
    private boolean mNoPerms;
    private boolean mSingleUser;
    private SparseBooleanArray mUsersRedirectedToOwner = new SparseBooleanArray();

    private ThreadLocal<AttributionSource> mCallingAttributionSource;

    /**
     * @hide
     */
    public static boolean isAuthorityRedirectedForCloneProfile(String authority) {
        // For now, only MediaProvider gets redirected.
        return MediaStore.AUTHORITY.equals(authority);
    }

    private Transport mTransport = new Transport();

    /**
@@ -726,13 +738,47 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
    }

    boolean checkUser(int pid, int uid, Context context) {
        if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
        int callingUserId = UserHandle.getUserId(uid);

        if (callingUserId == context.getUserId() || mSingleUser) {
            return true;
        }
        return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
        if (context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
                == PackageManager.PERMISSION_GRANTED
                || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
                    == PackageManager.PERMISSION_GRANTED;
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        }

        if (isAuthorityRedirectedForCloneProfile(mAuthority)) {
            if (mUsersRedirectedToOwner.indexOfKey(callingUserId) >= 0) {
                return mUsersRedirectedToOwner.get(callingUserId);
            }

            // Haven't seen this user yet, look it up
            try {
                UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
                Context callingUserContext = mContext.createPackageContextAsUser("system",
                        0, callingUser);
                UserManager um = callingUserContext.getSystemService(UserManager.class);

                if (um != null && um.isCloneProfile()) {
                    UserHandle parent = um.getProfileParent(callingUser);

                    if (parent != null && parent.equals(context.getUser())) {
                        mUsersRedirectedToOwner.put(callingUser.getIdentifier(), true);
                        return true;
                    }
                }
            } catch (PackageManager.NameNotFoundException e) {
                // ignore
            }

            mUsersRedirectedToOwner.put(UserHandle.getUserId(uid), false);
            return false;
        }

        return false;
    }

    /**
+36 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.server.am;

import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
import static android.os.Process.PROC_CHAR;
import static android.os.Process.PROC_OUT_LONG;
import static android.os.Process.PROC_PARENS;
@@ -46,6 +47,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -72,6 +74,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.RescueParty;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;

import java.io.FileDescriptor;
@@ -177,12 +180,22 @@ public class ContentProviderHelper {
                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
                if (cpr != null) {
                    cpi = cpr.info;

                    if (mService.isSingleton(
                            cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
                                && mService.isValidSingletonCall(
                                        r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
                        userId = UserHandle.USER_SYSTEM;
                        checkCrossUser = false;
                    } else if (isAuthorityRedirectedForCloneProfile(name)) {
                        UserManagerInternal umInternal = LocalServices.getService(
                                UserManagerInternal.class);
                        UserInfo userInfo = umInternal.getUserInfo(userId);

                        if (userInfo != null && userInfo.isCloneProfile()) {
                            userId = umInternal.getProfileParentId(userId);
                            checkCrossUser = false;
                        }
                    } else {
                        cpr = null;
                        cpi = null;
@@ -1026,6 +1039,7 @@ public class ContentProviderHelper {
     * at the given authority and user.
     */
    String checkContentProviderAccess(String authority, int userId) {
        boolean checkUser = true;
        if (userId == UserHandle.USER_ALL) {
            mService.mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
@@ -1041,6 +1055,17 @@ public class ContentProviderHelper {
                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                    userId);
            if (cpi == null && isAuthorityRedirectedForCloneProfile(authority)) {
                // This might be a provider that's running only in the system user that's
                // also serving clone profiles
                cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
                        ActivityManagerService.STOCK_PM_FLAGS
                                | PackageManager.GET_URI_PERMISSION_PATTERNS
                                | PackageManager.MATCH_DISABLED_COMPONENTS
                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                        UserHandle.USER_SYSTEM);
            }
        } catch (RemoteException ignored) {
        }
        if (cpi == null) {
@@ -1048,6 +1073,16 @@ public class ContentProviderHelper {
                    + "; expected to find a valid ContentProvider for this authority";
        }

        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 int callingPid = Binder.getCallingPid();
        ProcessRecord r;
        final String appName;
@@ -1060,7 +1095,7 @@ public class ContentProviderHelper {
        }

        return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(),
                userId, true, appName);
                userId, checkUser, appName);
    }

    int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
+26 −9
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.Manifest.permission.DELETE_PACKAGES;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
@@ -92,14 +93,6 @@ import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.PackageUserStateUtils;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -142,6 +135,14 @@ import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -4619,8 +4620,24 @@ public class ComputerEngine implements Computer {
            }
        }
        if (!checkedGrants) {
            enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider");
            boolean enforceCrossUser = true;

            if (isAuthorityRedirectedForCloneProfile(name)) {
                final UserManagerInternal umInternal = mInjector.getUserManagerInternal();

                UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
                if (userInfo != null && userInfo.isCloneProfile()
                        && userInfo.profileGroupId == userId) {
                    enforceCrossUser = false;
                }
            }

            if (enforceCrossUser) {
                enforceCrossUserPermission(callingUid, userId, false, false,
                        "resolveContentProvider");
            }
        }

        if (providerInfo == null) {
            return null;
        }