blob: 642ebe636ff4aba4dbe74042ea8a2b5285ee562f [file] [log] [blame]
/*
* Copyright (C) 2020 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.documentsui.base;
import static androidx.core.util.Preconditions.checkNotNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DocumentsContract;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.CursorLoader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.Objects;
/**
* Representation of a {@link UserHandle}.
*/
public final class UserId {
// A unspecified user is used as when the user's value is uninitialized. e.g. rootInfo.reset()
public static final UserId UNSPECIFIED_USER = UserId.of(UserHandle.of(-1000));
// A current user represents the user of the app's process. It is mainly used for comparison.
public static final UserId CURRENT_USER = UserId.of(Process.myUserHandle());
// A default user represents the user of the app's process. It is mainly used for operation
// which supports only the current user only.
public static final UserId DEFAULT_USER = CURRENT_USER;
private static final int VERSION_INIT = 1;
private final UserHandle mUserHandle;
private UserId(UserHandle userHandle) {
checkNotNull(userHandle);
mUserHandle = userHandle;
}
/**
* Returns a {@link UserId} for a given {@link UserHandle}.
*/
public static UserId of(UserHandle userHandle) {
return new UserId(userHandle);
}
/**
* Returns a {@link UserId} for the given user id identifier.
*
* @see UserHandle#getIdentifier
*/
public static UserId of(int userIdentifier) {
return of(UserHandle.of(userIdentifier));
}
/**
* Returns the given context if the user is the current user or unspecified. Otherwise, returns
* an "android" package context as the user.
*
* @throws IllegalStateException if android package of the other user does not exist
*/
@VisibleForTesting
Context asContext(Context context) {
if (CURRENT_USER.equals(this) || isUnspecified()) {
return context;
}
try {
return context.createPackageContextAsUser("android", /* flags= */ 0, mUserHandle);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException("android package not found.");
}
}
/**
* Return a package manager instance of this user.
*/
public PackageManager getPackageManager(Context context) {
return asContext(context).getPackageManager();
}
/**
* Return a content resolver instance of this user.
*/
public ContentResolver getContentResolver(Context context) {
return asContext(context).getContentResolver();
}
/**
* Returns a drawable object associated with a particular resource ID in this user.
*/
public Drawable getDrawable(Context context, int resId) {
return asContext(context).getDrawable(resId);
}
/**
* If this target user is a managed profile, then this returns a badged copy of the given icon
* to be able to distinguish it from the original icon.
*/
public Drawable getUserBadgedIcon(Context context, Drawable drawable) {
return getPackageManager(context).getUserBadgedIcon(drawable, mUserHandle);
}
/**
* Returns the value of {@link PackageManager#getUserBadgedLabel(CharSequence, UserHandle)} for
* the user and given label.
*/
public CharSequence getUserBadgedLabel(Context context, CharSequence label) {
return getPackageManager(context).getUserBadgedLabel(label, mUserHandle);
}
/**
* Returns true if this user refers to the system user; false otherwise.
*/
public boolean isSystem() {
return mUserHandle.isSystem();
}
/**
* Returns true if the this user is a managed profile.
*/
public boolean isManagedProfile(UserManager userManager) {
return userManager.isManagedProfile(mUserHandle.getIdentifier());
}
/**
* Returns true if the this user is in quiet mode.
*/
public boolean isQuietModeEnabled(Context context) {
final UserManager userManager =
(UserManager) context.getSystemService(Context.USER_SERVICE);
return userManager.isQuietModeEnabled(mUserHandle);
}
/**
* Disables quiet mode for a managed profile. The caller should check {@code
* MODIFY_QUIET_MODE} permission first.
*
* @return {@code false} if user's credential is needed in order to turn off quiet mode,
* {@code true} otherwise
*/
public boolean requestQuietModeDisabled(Context context) {
final UserManager userManager =
(UserManager) context.getSystemService(Context.USER_SERVICE);
return userManager.requestQuietModeEnabled(false, mUserHandle);
}
/**
* Returns a document uri representing this user.
*/
public Uri buildDocumentUriAsUser(String authority, String documentId) {
return DocumentsContract.buildDocumentUriAsUser(authority, documentId, mUserHandle);
}
/**
* Returns a tree document uri representing this user.
*/
public Uri buildTreeDocumentUriAsUser(String authority, String documentId) {
String authorityWithUserInfo = buildDocumentUriAsUser(authority, documentId).getAuthority();
Uri treeUri = DocumentsContract.buildTreeDocumentUri(authority, documentId);
return treeUri.buildUpon()
.encodedAuthority(authorityWithUserInfo)
.build();
}
/**
* Starts activity for this user
*/
public void startActivityAsUser(Context context, Intent intent) {
context.startActivityAsUser(intent, mUserHandle);
}
/**
* Returns an identifier stored in this user id. This can be used to recreate the {@link UserId}
* by {@link UserId#of(int)}.
*/
public int getIdentifier() {
return mUserHandle.getIdentifier();
}
private boolean isUnspecified() {
return UNSPECIFIED_USER.equals(this);
}
@Override
public String toString() {
return isUnspecified() ? "UNSPECIFIED" : String.valueOf(mUserHandle.getIdentifier());
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (o instanceof UserId) {
UserId other = (UserId) o;
return Objects.equals(mUserHandle, other.mUserHandle);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(mUserHandle);
}
/**
* Reads a {@link UserId} from an input stream.
*/
public static UserId read(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
int userId = in.readInt();
return UserId.of(UserHandle.of(userId));
default:
throw new ProtocolException("Unknown version " + version);
}
}
/**
* Writes a {@link UserId} to an output stream.
*/
public static void write(DataOutputStream out, UserId userId) throws IOException {
out.writeInt(VERSION_INIT);
out.writeInt(userId.mUserHandle.getIdentifier());
}
/**
* Create a cursor loader of the user for the given uri.
*/
public static CursorLoader createCursorLoader(Context context, Uri uri, UserId userId) {
return new CursorLoader(userId.asContext(context), uri, null, null, null, null);
}
}