1 /*
2  * Copyright (C) 2024 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.server.wm;
18 
19 import static android.os.Process.INVALID_UID;
20 
21 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
22 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
23 
24 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
25 import static org.xmlpull.v1.XmlPullParser.END_TAG;
26 import static org.xmlpull.v1.XmlPullParser.START_TAG;
27 
28 import android.content.ClipData;
29 import android.content.ContentProvider;
30 import android.content.ContentResolver;
31 import android.content.Intent;
32 import android.net.Uri;
33 import android.os.BadParcelableException;
34 import android.os.IBinder;
35 import android.os.UserHandle;
36 import android.util.ArraySet;
37 import android.util.Slog;
38 
39 import com.android.internal.util.XmlUtils;
40 import com.android.modules.utils.TypedXmlPullParser;
41 import com.android.modules.utils.TypedXmlSerializer;
42 import com.android.server.uri.GrantUri;
43 
44 import org.xmlpull.v1.XmlPullParserException;
45 
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.WeakHashMap;
49 
50 /**
51  * Represents the state of activity callers. Used by {@link ActivityRecord}.
52  * @hide
53  */
54 final class ActivityCallerState {
55     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityCallerState" : TAG_ATM;
56 
57     // XML tags for CallerInfo
58     private static final String ATTR_CALLER_UID = "caller_uid";
59     private static final String ATTR_CALLER_PACKAGE = "caller_package";
60     private static final String ATTR_CALLER_IS_SHARE_ENABLED = "caller_is_share_enabled";
61     private static final String TAG_READABLE_CONTENT_URI = "readable_content_uri";
62     private static final String TAG_WRITABLE_CONTENT_URI = "writable_content_uri";
63     private static final String TAG_INACCESSIBLE_CONTENT_URI = "inaccessible_content_uri";
64     private static final String ATTR_SOURCE_USER_ID = "source_user_id";
65     private static final String ATTR_URI = "uri";
66     private static final String ATTR_PREFIX = "prefix";
67 
68     // Map for storing CallerInfo instances
69     private final WeakHashMap<IBinder, CallerInfo> mCallerTokenInfoMap = new WeakHashMap<>();
70 
71     final ActivityTaskManagerService mAtmService;
72 
ActivityCallerState(ActivityTaskManagerService service)73     ActivityCallerState(ActivityTaskManagerService service) {
74         mAtmService = service;
75     }
76 
getCallerInfoOrNull(IBinder callerToken)77     CallerInfo getCallerInfoOrNull(IBinder callerToken) {
78         return mCallerTokenInfoMap.getOrDefault(callerToken, null);
79     }
80 
hasCaller(IBinder callerToken)81     boolean hasCaller(IBinder callerToken) {
82         return getCallerInfoOrNull(callerToken) != null;
83     }
84 
getUid(IBinder callerToken)85     int getUid(IBinder callerToken) {
86         CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
87         return callerInfo != null ? callerInfo.mUid : INVALID_UID;
88     }
89 
getPackage(IBinder callerToken)90     String getPackage(IBinder callerToken) {
91         CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
92         return callerInfo != null ? callerInfo.mPackageName : null;
93     }
94 
isShareIdentityEnabled(IBinder callerToken)95     boolean isShareIdentityEnabled(IBinder callerToken) {
96         CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
97         return callerInfo != null ? callerInfo.mIsShareIdentityEnabled : false;
98     }
99 
add(IBinder callerToken, CallerInfo callerInfo)100     void add(IBinder callerToken, CallerInfo callerInfo) {
101         mCallerTokenInfoMap.put(callerToken, callerInfo);
102     }
103 
computeCallerInfo(IBinder callerToken, Intent intent, int callerUid, String callerPackageName, boolean isCallerShareIdentityEnabled)104     void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid,
105             String callerPackageName, boolean isCallerShareIdentityEnabled) {
106         final CallerInfo callerInfo = new CallerInfo(callerUid, callerPackageName,
107                 isCallerShareIdentityEnabled);
108         mCallerTokenInfoMap.put(callerToken, callerInfo);
109 
110         final ArraySet<Uri> contentUris = getContentUrisFromIntent(intent);
111         for (int i = contentUris.size() - 1; i >= 0; i--) {
112             final Uri contentUri = contentUris.valueAt(i);
113 
114             final boolean hasRead = addContentUriIfUidHasPermission(contentUri, callerUid,
115                     Intent.FLAG_GRANT_READ_URI_PERMISSION, callerInfo.mReadableContentUris);
116 
117             final boolean hasWrite = addContentUriIfUidHasPermission(contentUri, callerUid,
118                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION, callerInfo.mWritableContentUris);
119 
120             if (!hasRead && !hasWrite) {
121                 callerInfo.mInaccessibleContentUris.add(convertToGrantUri(contentUri,
122                         /* modeFlags */ 0, callerUid));
123             }
124         }
125     }
126 
checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags)127     boolean checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags) {
128         if (!Intent.isAccessUriMode(modeFlags)) {
129             throw new IllegalArgumentException("Mode flags are not access URI mode flags: "
130                     + modeFlags);
131         }
132 
133         final CallerInfo callerInfo = mCallerTokenInfoMap.getOrDefault(callerToken, null);
134         if (callerInfo == null) {
135             Slog.e(TAG, "Caller not found for checkContentUriPermission of: "
136                     + grantUri.uri.toSafeString());
137             return false;
138         }
139 
140         if (callerInfo.mInaccessibleContentUris.contains(grantUri)) {
141             return false;
142         }
143 
144         final boolean readMet = callerInfo.mReadableContentUris.contains(grantUri);
145         final boolean writeMet = callerInfo.mWritableContentUris.contains(grantUri);
146 
147         if (!readMet && !writeMet) {
148             throw new IllegalArgumentException("The supplied URI wasn't passed at launch in"
149                     + " #getData, #EXTRA_STREAM, nor #getClipData: " + grantUri.uri.toSafeString());
150         }
151 
152         final boolean checkRead = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
153         if (checkRead && !readMet) {
154             return false;
155         }
156 
157         final boolean checkWrite = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
158         if (checkWrite && !writeMet) {
159             return false;
160         }
161 
162         return true;
163     }
164 
addContentUriIfUidHasPermission(Uri contentUri, int uid, int modeFlags, ArraySet<GrantUri> grantUris)165     private boolean addContentUriIfUidHasPermission(Uri contentUri, int uid, int modeFlags,
166             ArraySet<GrantUri> grantUris) {
167         final GrantUri grantUri = convertToGrantUri(contentUri, modeFlags, uid);
168         if (mAtmService.mUgmInternal.checkUriPermission(grantUri, uid,
169                 modeFlags, /* isFullAccessForContentUri */ true)) {
170             grantUris.add(grantUri);
171             return true;
172         }
173         return false;
174     }
175 
convertToGrantUri(Uri contentUri, int modeFlags, int uid)176     private static GrantUri convertToGrantUri(Uri contentUri, int modeFlags, int uid) {
177         return new GrantUri(ContentProvider.getUserIdFromUri(contentUri,
178                 UserHandle.getUserId(uid)), ContentProvider.getUriWithoutUserId(contentUri),
179                 modeFlags);
180     }
181 
getContentUrisFromIntent(Intent intent)182     private static ArraySet<Uri> getContentUrisFromIntent(Intent intent) {
183         final ArraySet<Uri> uris = new ArraySet<>();
184         if (intent == null) return uris;
185 
186         // getData
187         addUriIfContentUri(intent.getData(), uris);
188 
189         // EXTRA_STREAM
190         if (intent.hasExtra(Intent.EXTRA_STREAM)) {
191             final ArrayList<Uri> streams = tryToUnparcelArrayListExtraStreamsUri(intent);
192             if (streams == null) {
193                 addUriIfContentUri(tryToUnparcelExtraStreamUri(intent), uris);
194             } else {
195                 for (int i = streams.size() - 1; i >= 0; i--) {
196                     addUriIfContentUri(streams.get(i), uris);
197                 }
198             }
199         }
200 
201         final ClipData clipData = intent.getClipData();
202         if (clipData == null) return uris;
203 
204         for (int i = 0; i < clipData.getItemCount(); i++) {
205             final ClipData.Item item = clipData.getItemAt(i);
206 
207             // getUri
208             addUriIfContentUri(item.getUri(), uris);
209 
210             // getIntent
211             uris.addAll(getContentUrisFromIntent(item.getIntent()));
212         }
213         return uris;
214     }
215 
tryToUnparcelExtraStreamUri(Intent intent)216     private static Uri tryToUnparcelExtraStreamUri(Intent intent) {
217         try {
218             return intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class);
219         } catch (BadParcelableException e) {
220             // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
221             // instances of {@link BadParcelableException}, we still want to be on the safer side
222             // and catch the exception to ensure no breakages happen. If the unparcel fails, the
223             // item is still preserved with the underlying parcel.
224             Slog.w(TAG, "Failed to unparcel an URI in EXTRA_STREAM, returning null: " + e);
225             return null;
226         }
227     }
228 
tryToUnparcelArrayListExtraStreamsUri(Intent intent)229     private static ArrayList<Uri> tryToUnparcelArrayListExtraStreamsUri(Intent intent) {
230         try {
231             return intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri.class);
232         } catch (BadParcelableException e) {
233             // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
234             // instances of {@link BadParcelableException}, we still want to be on the safer side
235             // and catch the exception to ensure no breakages happen. If the unparcel fails, the
236             // item is still preserved with the underlying parcel.
237             Slog.w(TAG, "Failed to unparcel an ArrayList of URIs in EXTRA_STREAM, returning null: "
238                     + e);
239             return null;
240         }
241     }
242 
addUriIfContentUri(Uri uri, ArraySet<Uri> uris)243     private static void addUriIfContentUri(Uri uri, ArraySet<Uri> uris) {
244         if (uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
245             uris.add(uri);
246         }
247     }
248 
249     public static final class CallerInfo {
250         final int mUid;
251         final String mPackageName;
252         final boolean mIsShareIdentityEnabled;
253         final ArraySet<GrantUri> mReadableContentUris = new ArraySet<>();
254         final ArraySet<GrantUri> mWritableContentUris = new ArraySet<>();
255         final ArraySet<GrantUri> mInaccessibleContentUris = new ArraySet<>();
256 
CallerInfo(int uid, String packageName, boolean isShareIdentityEnabled)257         CallerInfo(int uid, String packageName, boolean isShareIdentityEnabled) {
258             mUid = uid;
259             mPackageName = packageName;
260             mIsShareIdentityEnabled = isShareIdentityEnabled;
261         }
262 
saveToXml(TypedXmlSerializer out)263         public void saveToXml(TypedXmlSerializer out)
264                 throws IOException, XmlPullParserException {
265             out.attributeInt(null, ATTR_CALLER_UID, mUid);
266             if (mPackageName != null) {
267                 out.attribute(null, ATTR_CALLER_PACKAGE, mPackageName);
268             }
269             out.attributeBoolean(null, ATTR_CALLER_IS_SHARE_ENABLED, mIsShareIdentityEnabled);
270             for (int i = mReadableContentUris.size() - 1; i >= 0; i--) {
271                 saveGrantUriToXml(out, mReadableContentUris.valueAt(i), TAG_READABLE_CONTENT_URI);
272             }
273 
274             for (int i = mWritableContentUris.size() - 1; i >= 0; i--) {
275                 saveGrantUriToXml(out, mWritableContentUris.valueAt(i), TAG_WRITABLE_CONTENT_URI);
276             }
277 
278             for (int i = mInaccessibleContentUris.size() - 1; i >= 0; i--) {
279                 saveGrantUriToXml(out, mInaccessibleContentUris.valueAt(i),
280                         TAG_INACCESSIBLE_CONTENT_URI);
281             }
282         }
283 
restoreFromXml(TypedXmlPullParser in)284         public static CallerInfo restoreFromXml(TypedXmlPullParser in)
285                 throws IOException, XmlPullParserException {
286             int uid = in.getAttributeInt(null, ATTR_CALLER_UID, 0);
287             String packageName = in.getAttributeValue(null, ATTR_CALLER_PACKAGE);
288             boolean isShareIdentityEnabled = in.getAttributeBoolean(null,
289                     ATTR_CALLER_IS_SHARE_ENABLED, false);
290 
291             CallerInfo callerInfo = new CallerInfo(uid, packageName, isShareIdentityEnabled);
292             final int outerDepth = in.getDepth();
293             int event;
294             while (((event = in.next()) != END_DOCUMENT)
295                     && (event != END_TAG || in.getDepth() >= outerDepth)) {
296                 if (event == START_TAG) {
297                     final String name = in.getName();
298                     if (TAG_READABLE_CONTENT_URI.equals(name)) {
299                         callerInfo.mReadableContentUris.add(restoreGrantUriFromXml(in));
300                     } else if (TAG_WRITABLE_CONTENT_URI.equals(name)) {
301                         callerInfo.mWritableContentUris.add(restoreGrantUriFromXml(in));
302                     } else if (TAG_INACCESSIBLE_CONTENT_URI.equals(name)) {
303                         callerInfo.mInaccessibleContentUris.add(restoreGrantUriFromXml(in));
304                     } else {
305                         Slog.w(TAG, "restoreActivity: unexpected name=" + name);
306                         XmlUtils.skipCurrentTag(in);
307                     }
308                 }
309             }
310             return callerInfo;
311         }
312 
saveGrantUriToXml(TypedXmlSerializer out, GrantUri grantUri, String tag)313         private void saveGrantUriToXml(TypedXmlSerializer out, GrantUri grantUri, String tag)
314                 throws IOException, XmlPullParserException {
315             out.startTag(null, tag);
316             out.attributeInt(null, ATTR_SOURCE_USER_ID, grantUri.sourceUserId);
317             out.attribute(null, ATTR_URI, String.valueOf(grantUri.uri));
318             out.attributeBoolean(null, ATTR_PREFIX, grantUri.prefix);
319             out.endTag(null, tag);
320         }
321 
restoreGrantUriFromXml(TypedXmlPullParser in)322         private static GrantUri restoreGrantUriFromXml(TypedXmlPullParser in)
323                 throws IOException, XmlPullParserException {
324             int sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID, 0);
325             Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
326             boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false);
327             return new GrantUri(sourceUserId, uri,
328                     prefix ? Intent.FLAG_GRANT_PREFIX_URI_PERMISSION : 0);
329         }
330     }
331 }
332