blob: f2cded547177c0758721e0b8816a75a4303cbd32 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.providers.media.photopicker.data;
import static androidx.core.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.providers.media.photopicker.data.model.UserId;
import com.android.providers.media.photopicker.util.CrossProfileUtils;
import java.util.List;
/**
* Interface to query user ids {@link UserId}
*/
public interface UserIdManager {
/**
* Whether there are more than 1 user profiles associated with the current user.
* @return
*/
boolean isMultiUserProfiles();
/**
* Returns the personal user profile id iff there are at least 2 user profiles for current
* user. Otherwise, returns null.
*/
@Nullable
UserId getPersonalUserId();
/**
* Returns the managed user profile id iff there are at least 2 user profiles for current user.
* Otherwise, returns null.
*/
@Nullable
UserId getManagedUserId();
/**
* Returns the current user profile id. This can be managed user profile id, personal user
* profile id. If the user does not have a corresponding managed profile, then this always
* returns the current user.
*/
@Nullable
UserId getCurrentUserProfileId();
/**
* Set Managed User as current user profile
*/
void setManagedAsCurrentUserProfile();
/**
* Set Personal User as current user profile
*/
void setPersonalAsCurrentUserProfile();
/**
* @return true iff current user is the current user profile selected
*/
boolean isCurrentUserSelected();
/**
* @return true iff managed user is the current user profile selected
*/
boolean isManagedUserSelected();
/**
* Whether the current user is the personal user profile iff there are at least 2 user
* profiles for current user. Otherwise, returns false.
*/
boolean isPersonalUserId();
/**
* Whether the current user is the managed user profile iff there are at least 2 user
* profiles for current user. Otherwise, returns false.
*/
boolean isManagedUserId();
/**
* Whether the current user is allowed to access other profile data.
*/
boolean isCrossProfileAllowed();
/**
* Whether cross profile access is blocked by admin for the current user.
*/
boolean isBlockedByAdmin();
/**
* Whether the work profile corresponding to the current user is turned off.
*/
boolean isWorkProfileOff();
/**
* Set intent to check for device admin policy.
*/
void setIntentAndCheckRestrictions(Intent intent);
/**
* Creates an implementation of {@link UserIdManager}.
*/
static UserIdManager create(Context context) {
return new RuntimeUserIdManager(context);
}
/**
* Implementation of {@link UserIdManager}.
*/
final class RuntimeUserIdManager implements UserIdManager {
private static final String TAG = "UserIdManager";
private final Context mContext;
private final UserId mCurrentUser;
@GuardedBy("mLock")
private final Object mLock = new Object();
@GuardedBy("mLock")
private UserId mPersonalUser = null;
@GuardedBy("mLock")
private UserId mManagedUser = null;
@GuardedBy("mLock")
private UserId mCurrentUserProfile = null;
private Intent mIntent = null;
// Set default values to negative case, only set as false if checks pass.
@GuardedBy("mLock")
private boolean mIsBlockedByAdmin = true;
@GuardedBy("mLock")
private boolean mIsWorkProfileOff = true;
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
mPersonalUser = null;
mManagedUser = null;
setUserIds();
}
}
};
private RuntimeUserIdManager(Context context) {
this(context, UserId.CURRENT_USER);
}
@VisibleForTesting
RuntimeUserIdManager(Context context, UserId currentUser) {
mContext = context.getApplicationContext();
mCurrentUser = checkNotNull(currentUser);
mCurrentUserProfile = mCurrentUser;
setUserIds();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiver(mIntentReceiver, filter);
}
@Override
public boolean isMultiUserProfiles() {
synchronized (mLock) {
return mPersonalUser != null;
}
}
@Override
public UserId getPersonalUserId() {
synchronized (mLock) {
return mPersonalUser;
}
}
@Override
public UserId getManagedUserId() {
synchronized (mLock) {
return mManagedUser;
}
}
@Override
public UserId getCurrentUserProfileId() {
synchronized (mLock) {
return mCurrentUserProfile;
}
}
@Override
public void setManagedAsCurrentUserProfile() {
setCurrentUserProfileId(getManagedUserId());
}
@Override
public void setPersonalAsCurrentUserProfile() {
setCurrentUserProfileId(getPersonalUserId());
}
@Override
public void setIntentAndCheckRestrictions(Intent intent) {
mIntent = intent;
synchronized (mLock) {
setCrossProfileValues();
}
}
public boolean isCurrentUserSelected() {
synchronized (mLock) {
return mCurrentUserProfile.equals(UserId.CURRENT_USER);
}
}
public boolean isManagedUserSelected() {
synchronized (mLock) {
return mCurrentUserProfile.equals(getManagedUserId());
}
}
@Override
public boolean isPersonalUserId() {
return mCurrentUser.equals(getPersonalUserId());
}
@Override
public boolean isManagedUserId() {
return mCurrentUser.equals(getManagedUserId());
}
private void setUserIds() {
synchronized (mLock) {
setUserIdsInternal();
setCrossProfileValues();
}
}
private void setCurrentUserProfileId(UserId userId) {
synchronized (mLock) {
mCurrentUserProfile = userId;
}
}
@GuardedBy("mLock")
private void setUserIdsInternal() {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager == null) {
Log.e(TAG, "Cannot obtain user manager");
return;
}
final List<UserHandle> userProfiles = userManager.getUserProfiles();
if (userProfiles.size() < 2) {
Log.d(TAG, "Only 1 user profile found");
return;
}
if (mCurrentUser.isManagedProfile(userManager)) {
final UserId managedUser = mCurrentUser;
final UserHandle parentUser =
userManager.getProfileParent(managedUser.getUserHandle());
if (parentUser != null) {
mPersonalUser = UserId.of(parentUser);
mManagedUser = managedUser;
}
} else {
final UserId personalUser = mCurrentUser;
// Check if this personal profile is a parent of any other managed profile.
for (UserHandle userHandle : userProfiles) {
if (userManager.isManagedProfile(userHandle.getIdentifier())) {
final UserHandle parentUser =
userManager.getProfileParent(userHandle);
if (parentUser != null &&
parentUser.equals(personalUser.getUserHandle())) {
mPersonalUser = personalUser;
mManagedUser = UserId.of(userHandle);
}
}
}
}
}
@Override
public boolean isCrossProfileAllowed() {
return (!isWorkProfileOff() && !isBlockedByAdmin());
}
@Override
public boolean isWorkProfileOff() {
synchronized (mLock) {
return mIsWorkProfileOff;
}
}
@Override
public boolean isBlockedByAdmin() {
synchronized (mLock) {
return mIsBlockedByAdmin;
}
}
@GuardedBy("mLock")
private void setCrossProfileValues() {
final PackageManager packageManager = mContext.getPackageManager();
// 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
if (mIntent == null) {
Log.e(TAG, "No intent specified to check if cross profile forwarding is"
+ " allowed.");
return;
}
if (!CrossProfileUtils.isIntentAllowedCrossProfileAccess(mIntent, packageManager)) {
mIsBlockedByAdmin = true;
return;
}
mIsBlockedByAdmin = false;
// 2. Check if work profile is off
if (!isManagedUserSelected()) {
final UserId managedUserProfileId = getManagedUserId();
if (!CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId, mContext)) {
mIsWorkProfileOff = true;
return;
}
}
mIsWorkProfileOff = false;
}
}
}