/* * 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); } }