blob: a5d47386afb5014efb3091e4879f2daae48bfffb [file] [log] [blame]
Wale Ogunwaleee6eca12018-09-19 20:37:53 -07001/*
2 * Copyright (C) 2018 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
17package com.android.server.am;
18
19import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
20import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
21import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
22import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
23import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
24
25import android.app.Activity;
26import android.app.ActivityManagerInternal;
27import android.app.AppGlobals;
28import android.app.PendingIntent;
29import android.content.IIntentSender;
30import android.content.Intent;
31import android.os.Binder;
32import android.os.Bundle;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.Looper;
36import android.os.Message;
37import android.os.RemoteCallbackList;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.util.ArrayMap;
41import android.util.Slog;
42import com.android.internal.os.IResultReceiver;
43import com.android.internal.util.function.pooled.PooledLambda;
44import com.android.server.LocalServices;
45import com.android.server.wm.ActivityTaskManagerInternal;
Wale Ogunwale59507092018-10-29 09:00:30 -070046import com.android.server.wm.SafeActivityOptions;
Wale Ogunwaleee6eca12018-09-19 20:37:53 -070047
48import java.io.PrintWriter;
49import java.lang.ref.WeakReference;
50import java.util.ArrayList;
51import java.util.HashMap;
52import java.util.Iterator;
53
54/**
55 * Helper class for {@link ActivityManagerService} responsible for managing pending intents.
56 *
57 * <p>This class uses {@link #mLock} to synchronize access to internal state and doesn't make use of
58 * {@link ActivityManagerService} lock since there can be direct calls into this class from outside
59 * AM. This helps avoid deadlocks.
60 */
61public class PendingIntentController {
62 private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM;
63 private static final String TAG_MU = TAG + POSTFIX_MU;
64
65 /** Lock for internal state. */
66 final Object mLock = new Object();
67 final Handler mH;
68 ActivityManagerInternal mAmInternal;
69 final UserController mUserController;
70 final ActivityTaskManagerInternal mAtmInternal;
71
72 /** Set of IntentSenderRecord objects that are currently active. */
73 final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
74 = new HashMap<>();
75
76 PendingIntentController(Looper looper, UserController userController) {
77 mH = new Handler(looper);
78 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
79 mUserController = userController;
80 }
81
82 void onActivityManagerInternalAdded() {
83 synchronized (mLock) {
84 mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
85 }
86 }
87
Wale Ogunwale59507092018-10-29 09:00:30 -070088 public PendingIntentRecord getIntentSender(int type, String packageName, int callingUid,
89 int userId, IBinder token, String resultWho, int requestCode, Intent[] intents,
Wale Ogunwaleee6eca12018-09-19 20:37:53 -070090 String[] resolvedTypes, int flags, Bundle bOptions) {
91 synchronized (mLock) {
92 if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
93
94 // We're going to be splicing together extras before sending, so we're
95 // okay poking into any contained extras.
96 if (intents != null) {
97 for (int i = 0; i < intents.length; i++) {
98 intents[i].setDefusable(true);
99 }
100 }
101 Bundle.setDefusable(bOptions, true);
102
103 final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
104 final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
105 final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0;
106 flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
107 | PendingIntent.FLAG_UPDATE_CURRENT);
108
109 PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token,
110 resultWho, requestCode, intents, resolvedTypes, flags,
111 SafeActivityOptions.fromBundle(bOptions), userId);
112 WeakReference<PendingIntentRecord> ref;
113 ref = mIntentSenderRecords.get(key);
114 PendingIntentRecord rec = ref != null ? ref.get() : null;
115 if (rec != null) {
116 if (!cancelCurrent) {
117 if (updateCurrent) {
118 if (rec.key.requestIntent != null) {
119 rec.key.requestIntent.replaceExtras(intents != null ?
120 intents[intents.length - 1] : null);
121 }
122 if (intents != null) {
123 intents[intents.length - 1] = rec.key.requestIntent;
124 rec.key.allIntents = intents;
125 rec.key.allResolvedTypes = resolvedTypes;
126 } else {
127 rec.key.allIntents = null;
128 rec.key.allResolvedTypes = null;
129 }
130 }
131 return rec;
132 }
133 makeIntentSenderCanceled(rec);
134 mIntentSenderRecords.remove(key);
135 }
136 if (noCreate) {
137 return rec;
138 }
139 rec = new PendingIntentRecord(this, key, callingUid);
140 mIntentSenderRecords.put(key, rec.ref);
141 return rec;
142 }
143 }
144
145 boolean removePendingIntentsForPackage(String packageName, int userId, int appId,
146 boolean doIt) {
147
148 boolean didSomething = false;
149 synchronized (mLock) {
150
151 // Remove pending intents. For now we only do this when force stopping users, because
152 // we have some problems when doing this for packages -- app widgets are not currently
153 // cleaned up for such packages, so they can be left with bad pending intents.
154 if (mIntentSenderRecords.size() <= 0) {
155 return false;
156 }
157
158 Iterator<WeakReference<PendingIntentRecord>> it
159 = mIntentSenderRecords.values().iterator();
160 while (it.hasNext()) {
161 WeakReference<PendingIntentRecord> wpir = it.next();
162 if (wpir == null) {
163 it.remove();
164 continue;
165 }
166 PendingIntentRecord pir = wpir.get();
167 if (pir == null) {
168 it.remove();
169 continue;
170 }
171 if (packageName == null) {
172 // Stopping user, remove all objects for the user.
173 if (pir.key.userId != userId) {
174 // Not the same user, skip it.
175 continue;
176 }
177 } else {
178 if (UserHandle.getAppId(pir.uid) != appId) {
179 // Different app id, skip it.
180 continue;
181 }
182 if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
183 // Different user, skip it.
184 continue;
185 }
186 if (!pir.key.packageName.equals(packageName)) {
187 // Different package, skip it.
188 continue;
189 }
190 }
191 if (!doIt) {
192 return true;
193 }
194 didSomething = true;
195 it.remove();
196 makeIntentSenderCanceled(pir);
197 if (pir.key.activity != null) {
198 final Message m = PooledLambda.obtainMessage(
199 PendingIntentController::clearPendingResultForActivity, this,
200 pir.key.activity, pir.ref);
201 mH.sendMessage(m);
202 }
203 }
204 }
205
206 return didSomething;
207 }
208
209 public void cancelIntentSender(IIntentSender sender) {
210 if (!(sender instanceof PendingIntentRecord)) {
211 return;
212 }
213 synchronized (mLock) {
214 final PendingIntentRecord rec = (PendingIntentRecord) sender;
215 try {
216 final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
217 MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
218 if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
219 String msg = "Permission Denial: cancelIntentSender() from pid="
220 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
221 + " is not allowed to cancel package " + rec.key.packageName;
222 Slog.w(TAG, msg);
223 throw new SecurityException(msg);
224 }
225 } catch (RemoteException e) {
226 throw new SecurityException(e);
227 }
228 cancelIntentSender(rec, true);
229 }
230 }
231
232 public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) {
233 synchronized (mLock) {
234 makeIntentSenderCanceled(rec);
235 mIntentSenderRecords.remove(rec.key);
236 if (cleanActivity && rec.key.activity != null) {
237 final Message m = PooledLambda.obtainMessage(
238 PendingIntentController::clearPendingResultForActivity, this,
239 rec.key.activity, rec.ref);
240 mH.sendMessage(m);
241 }
242 }
243 }
244
245 private void makeIntentSenderCanceled(PendingIntentRecord rec) {
246 rec.canceled = true;
247 final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
248 if (callbacks != null) {
249 final Message m = PooledLambda.obtainMessage(
250 PendingIntentController::handlePendingIntentCancelled, this, callbacks);
251 mH.sendMessage(m);
252 }
253 }
254
255 private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) {
256 int N = callbacks.beginBroadcast();
257 for (int i = 0; i < N; i++) {
258 try {
259 callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
260 } catch (RemoteException e) {
261 // Process is not longer running...whatever.
262 }
263 }
264 callbacks.finishBroadcast();
265 // We have to clean up the RemoteCallbackList here, because otherwise it will
266 // needlessly hold the enclosed callbacks until the remote process dies.
267 callbacks.kill();
268 }
269
270 private void clearPendingResultForActivity(IBinder activityToken,
271 WeakReference<PendingIntentRecord> pir) {
272 mAtmInternal.clearPendingResultForActivity(activityToken, pir);
273 }
274
275 void dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage) {
276 synchronized (mLock) {
277 boolean printed = false;
278
279 pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
280
281 if (mIntentSenderRecords.size() > 0) {
282 // Organize these by package name, so they are easier to read.
283 final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
284 final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
285 final Iterator<WeakReference<PendingIntentRecord>> it
286 = mIntentSenderRecords.values().iterator();
287 while (it.hasNext()) {
288 WeakReference<PendingIntentRecord> ref = it.next();
289 PendingIntentRecord rec = ref != null ? ref.get() : null;
290 if (rec == null) {
291 weakRefs.add(ref);
292 continue;
293 }
294 if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
295 continue;
296 }
297 ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
298 if (list == null) {
299 list = new ArrayList<>();
300 byPackage.put(rec.key.packageName, list);
301 }
302 list.add(rec);
303 }
304 for (int i = 0; i < byPackage.size(); i++) {
305 ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
306 printed = true;
307 pw.print(" * "); pw.print(byPackage.keyAt(i));
308 pw.print(": "); pw.print(intents.size()); pw.println(" items");
309 for (int j = 0; j < intents.size(); j++) {
310 pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
311 if (dumpAll) {
312 intents.get(j).dump(pw, " ");
313 }
314 }
315 }
316 if (weakRefs.size() > 0) {
317 printed = true;
318 pw.println(" * WEAK REFS:");
319 for (int i = 0; i < weakRefs.size(); i++) {
320 pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
321 }
322 }
323 }
324
325 if (!printed) {
326 pw.println(" (nothing)");
327 }
328 }
329 }
330}