• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.providers.media.util;
18 
19 import android.content.Context;
20 import android.content.pm.PackageManager;
21 import android.os.Process;
22 import android.os.UserHandle;
23 import android.os.UserManager;
24 import android.util.LongSparseArray;
25 
26 import androidx.annotation.GuardedBy;
27 import androidx.annotation.NonNull;
28 
29 import com.android.modules.utils.build.SdkLevel;
30 
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * UserCache is a class that keeps track of all users that the current MediaProvider
37  * instance is responsible for. By default, it handles storage for the user it is running as,
38  * but as of Android API 31, it will also handle storage for profiles that share media
39  * with their parent - profiles for which @link{UserManager#isMediaSharedWithParent} is set.
40  *
41  * It also keeps a cache of user contexts, for improving these lookups.
42  *
43  * Note that we don't use the USER_ broadcasts for keeping this state up to date, because they
44  * aren't guaranteed to be received before the volume events for a user.
45  */
46 public class UserCache {
47     final Object mLock = new Object();
48     final Context mContext;
49     final UserManager mUserManager;
50 
51     @GuardedBy("mLock")
52     final LongSparseArray<Context> mUserContexts = new LongSparseArray<>();
53 
54     @GuardedBy("mLock")
55     final ArrayList<UserHandle> mUsers = new ArrayList<>();
56 
UserCache(Context context)57     public UserCache(Context context) {
58         mContext = context;
59         mUserManager = context.getSystemService(UserManager.class);
60 
61         update();
62     }
63 
update()64     private void update() {
65         List<UserHandle> profiles = mUserManager.getEnabledProfiles();
66         synchronized (mLock) {
67             mUsers.clear();
68             // Add the user we're running as by default
69             mUsers.add(Process.myUserHandle());
70             if (!SdkLevel.isAtLeastS()) {
71                 // Before S, we only handle the owner user
72                 return;
73             }
74             // And find all profiles that share media with us
75             for (UserHandle profile : profiles) {
76                 if (!profile.equals(mContext.getUser())) {
77                     // Check if it's a profile that shares media with us
78                     Context userContext = getContextForUser(profile);
79                     if (userContext.getSystemService(UserManager.class).isMediaSharedWithParent()) {
80                         mUsers.add(profile);
81                     }
82                 }
83             }
84         }
85     }
86 
updateAndGetUsers()87     public @NonNull List<UserHandle> updateAndGetUsers() {
88         update();
89         synchronized (mLock) {
90             return (List<UserHandle>) mUsers.clone();
91         }
92     }
93 
getUsersCached()94     public @NonNull List<UserHandle> getUsersCached() {
95         synchronized (mLock) {
96             return (List<UserHandle>) mUsers.clone();
97         }
98     }
99 
getContextForUser(@onNull UserHandle user)100     public @NonNull Context getContextForUser(@NonNull UserHandle user) {
101         Context userContext;
102         synchronized (mLock) {
103             userContext = mUserContexts.get(user.getIdentifier());
104             if (userContext != null) {
105                 return userContext;
106             }
107         }
108         try {
109             userContext = mContext.createPackageContextAsUser("system", 0, user);
110             synchronized (mLock) {
111                 mUserContexts.put(user.getIdentifier(), userContext);
112             }
113             return userContext;
114         } catch (PackageManager.NameNotFoundException e) {
115             throw new RuntimeException("Failed to create context for user " + user, e);
116         }
117     }
118 
119     /**
120      *  Returns whether the passed in user shares media with its parent (or peer).
121      *
122      * @param user user to check
123      * @return whether the user shares media with its parent
124      */
userSharesMediaWithParent(@onNull UserHandle user)125     public boolean userSharesMediaWithParent(@NonNull UserHandle user) {
126         if (Process.myUserHandle().equals(user)) {
127             // Early return path - the owner user doesn't have a parent
128             return false;
129         }
130         boolean found = userSharesMediaWithParentCached(user);
131         if (!found) {
132             // Update the cache and try again
133             update();
134             found = userSharesMediaWithParentCached(user);
135         }
136         return found;
137     }
138 
139     /**
140      *  Returns whether the passed in user shares media with its parent (or peer).
141      *  Note that the value returned here is based on cached data; it relies on
142      *  other callers to keep the user cache up-to-date.
143      *
144      * @param user user to check
145      * @return whether the user shares media with its parent
146      */
userSharesMediaWithParentCached(@onNull UserHandle user)147     public boolean userSharesMediaWithParentCached(@NonNull UserHandle user) {
148         synchronized (mLock) {
149             // It must be a user that we manage, and not equal to the main user that we run as
150             return !Process.myUserHandle().equals(user) && mUsers.contains(user);
151         }
152     }
153 
dump(PrintWriter writer)154     public void dump(PrintWriter writer) {
155         writer.println("User cache state:");
156         synchronized (mLock) {
157             for (UserHandle user : mUsers) {
158                 writer.println("  user: " + user);
159             }
160         }
161     }
162 }
163