1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.documentsui.base; 18 19 import static androidx.core.util.Preconditions.checkNotNull; 20 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.graphics.drawable.Drawable; 26 import android.net.Uri; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.provider.DocumentsContract; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.loader.content.CursorLoader; 34 35 import java.io.DataInputStream; 36 import java.io.DataOutputStream; 37 import java.io.IOException; 38 import java.net.ProtocolException; 39 import java.util.Objects; 40 /** 41 * Representation of a {@link UserHandle}. 42 */ 43 public final class UserId { 44 45 // A unspecified user is used as when the user's value is uninitialized. e.g. rootInfo.reset() 46 public static final UserId UNSPECIFIED_USER = UserId.of(UserHandle.of(-1000)); 47 // A current user represents the user of the app's process. It is mainly used for comparison. 48 public static final UserId CURRENT_USER = UserId.of(Process.myUserHandle()); 49 // A default user represents the user of the app's process. It is mainly used for operation 50 // which supports only the current user only. 51 public static final UserId DEFAULT_USER = CURRENT_USER; 52 53 private static final int VERSION_INIT = 1; 54 55 private final UserHandle mUserHandle; 56 UserId(UserHandle userHandle)57 private UserId(UserHandle userHandle) { 58 checkNotNull(userHandle); 59 mUserHandle = userHandle; 60 } 61 62 /** 63 * Returns a {@link UserId} for a given {@link UserHandle}. 64 */ of(UserHandle userHandle)65 public static UserId of(UserHandle userHandle) { 66 return new UserId(userHandle); 67 } 68 69 /** 70 * Returns a {@link UserId} for the given user id identifier. 71 * 72 * @see UserHandle#getIdentifier 73 */ of(int userIdentifier)74 public static UserId of(int userIdentifier) { 75 return of(UserHandle.of(userIdentifier)); 76 } 77 78 /** 79 * Returns the given context if the user is the current user or unspecified. Otherwise, returns 80 * an "android" package context as the user. 81 * 82 * @throws IllegalStateException if android package of the other user does not exist 83 */ 84 @VisibleForTesting asContext(Context context)85 Context asContext(Context context) { 86 if (CURRENT_USER.equals(this) || isUnspecified()) { 87 return context; 88 } 89 try { 90 return context.createPackageContextAsUser("android", /* flags= */ 0, mUserHandle); 91 } catch (PackageManager.NameNotFoundException e) { 92 throw new IllegalStateException("android package not found."); 93 } 94 95 } 96 97 /** 98 * Return this User's {@link UserHandle}. 99 */ getUserHandle()100 public UserHandle getUserHandle() { 101 return mUserHandle; 102 } 103 104 /** 105 * Return a package manager instance of this user. 106 */ getPackageManager(Context context)107 public PackageManager getPackageManager(Context context) { 108 return asContext(context).getPackageManager(); 109 } 110 111 /** 112 * Return a content resolver instance of this user. 113 */ getContentResolver(Context context)114 public ContentResolver getContentResolver(Context context) { 115 return asContext(context).getContentResolver(); 116 } 117 118 /** 119 * Returns a drawable object associated with a particular resource ID in this user. 120 */ getDrawable(Context context, int resId)121 public Drawable getDrawable(Context context, int resId) { 122 return asContext(context).getDrawable(resId); 123 } 124 125 /** 126 * If this target user is a managed profile, then this returns a badged copy of the given icon 127 * to be able to distinguish it from the original icon. 128 */ getUserBadgedIcon(Context context, Drawable drawable)129 public Drawable getUserBadgedIcon(Context context, Drawable drawable) { 130 return getPackageManager(context).getUserBadgedIcon(drawable, mUserHandle); 131 } 132 133 /** 134 * Returns the value of {@link PackageManager#getUserBadgedLabel(CharSequence, UserHandle)} for 135 * the user and given label. 136 */ getUserBadgedLabel(Context context, CharSequence label)137 public CharSequence getUserBadgedLabel(Context context, CharSequence label) { 138 return getPackageManager(context).getUserBadgedLabel(label, mUserHandle); 139 } 140 141 /** 142 * Returns true if this user refers to the system user; false otherwise. 143 */ isSystem()144 public boolean isSystem() { 145 return mUserHandle.isSystem(); 146 } 147 148 /** 149 * Returns true if the this user is a managed profile. 150 */ isManagedProfile(UserManager userManager)151 public boolean isManagedProfile(UserManager userManager) { 152 return userManager.isManagedProfile(mUserHandle.getIdentifier()); 153 } 154 155 /** 156 * Returns true if the this user is in quiet mode. 157 */ isQuietModeEnabled(Context context)158 public boolean isQuietModeEnabled(Context context) { 159 final UserManager userManager = context.getSystemService(UserManager.class); 160 assert userManager != null; 161 return userManager.isQuietModeEnabled(mUserHandle); 162 } 163 164 /** 165 * Disables quiet mode for a managed profile. The caller should check {@code 166 * MODIFY_QUIET_MODE} permission first. 167 * 168 * @return {@code false} if user's credential is needed in order to turn off quiet mode, 169 * {@code true} otherwise 170 */ requestQuietModeDisabled(Context context)171 public boolean requestQuietModeDisabled(Context context) { 172 final UserManager userManager = 173 (UserManager) context.getSystemService(Context.USER_SERVICE); 174 return userManager.requestQuietModeEnabled(false, mUserHandle); 175 } 176 177 /** 178 * Returns a document uri representing this user. 179 */ buildDocumentUriAsUser(String authority, String documentId)180 public Uri buildDocumentUriAsUser(String authority, String documentId) { 181 return DocumentsContract.buildDocumentUriAsUser(authority, documentId, mUserHandle); 182 } 183 184 /** 185 * Returns a tree document uri representing this user. 186 */ buildTreeDocumentUriAsUser(String authority, String documentId)187 public Uri buildTreeDocumentUriAsUser(String authority, String documentId) { 188 String authorityWithUserInfo = buildDocumentUriAsUser(authority, documentId).getAuthority(); 189 Uri treeUri = DocumentsContract.buildTreeDocumentUri(authority, documentId); 190 191 return treeUri.buildUpon() 192 .encodedAuthority(authorityWithUserInfo) 193 .build(); 194 } 195 196 /** 197 * Starts activity for this user 198 */ startActivityAsUser(Context context, Intent intent)199 public void startActivityAsUser(Context context, Intent intent) { 200 context.startActivityAsUser(intent, mUserHandle); 201 } 202 203 /** 204 * Returns an identifier stored in this user id. This can be used to recreate the {@link UserId} 205 * by {@link UserId#of(int)}. 206 */ getIdentifier()207 public int getIdentifier() { 208 return mUserHandle.getIdentifier(); 209 } 210 isUnspecified()211 private boolean isUnspecified() { 212 return UNSPECIFIED_USER.equals(this); 213 } 214 215 @Override toString()216 public String toString() { 217 return isUnspecified() ? "UNSPECIFIED" : String.valueOf(mUserHandle.getIdentifier()); 218 } 219 220 @Override equals(Object o)221 public boolean equals(Object o) { 222 if (o == null) { 223 return false; 224 } 225 226 if (this == o) { 227 return true; 228 } 229 230 if (o instanceof UserId) { 231 UserId other = (UserId) o; 232 return Objects.equals(mUserHandle, other.mUserHandle); 233 } 234 235 return false; 236 } 237 238 @Override hashCode()239 public int hashCode() { 240 return Objects.hash(mUserHandle); 241 } 242 243 /** 244 * Reads a {@link UserId} from an input stream. 245 */ read(DataInputStream in)246 public static UserId read(DataInputStream in) throws IOException { 247 final int version = in.readInt(); 248 switch (version) { 249 case VERSION_INIT: 250 int userId = in.readInt(); 251 return UserId.of(UserHandle.of(userId)); 252 default: 253 throw new ProtocolException("Unknown version " + version); 254 } 255 } 256 257 /** 258 * Writes a {@link UserId} to an output stream. 259 */ write(DataOutputStream out, UserId userId)260 public static void write(DataOutputStream out, UserId userId) throws IOException { 261 out.writeInt(VERSION_INIT); 262 out.writeInt(userId.mUserHandle.getIdentifier()); 263 } 264 265 /** 266 * Create a cursor loader of the user for the given uri. 267 */ createCursorLoader(Context context, Uri uri, UserId userId)268 public static CursorLoader createCursorLoader(Context context, Uri uri, UserId userId) { 269 return new CursorLoader(userId.asContext(context), uri, null, null, null, null); 270 } 271 } 272