1/*
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.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
20import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
21import static android.content.pm.PackageManager.PERMISSION_DENIED;
22import static android.content.pm.PackageManager.PERMISSION_GRANTED;
23import static android.view.Display.INVALID_DISPLAY;
24import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
25import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
26import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
27
28import android.annotation.Nullable;
29import android.app.ActivityOptions;
30import android.app.PendingIntent;
31import android.content.Intent;
32import android.content.pm.ActivityInfo;
33import android.os.Binder;
34import android.os.Bundle;
35import android.os.Process;
36import android.os.UserHandle;
37import android.util.Slog;
38import android.view.RemoteAnimationAdapter;
39
40import com.android.internal.annotations.VisibleForTesting;
41
42/**
43 * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
44 * the inner options. Also supports having two set of options: Once from the original caller, and
45 * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
46 */
47class SafeActivityOptions {
48
49    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
50
51    private final int mOriginalCallingPid;
52    private final int mOriginalCallingUid;
53    private int mRealCallingPid;
54    private int mRealCallingUid;
55    private final @Nullable ActivityOptions mOriginalOptions;
56    private @Nullable ActivityOptions mCallerOptions;
57
58    /**
59     * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
60     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
61     * this object.
62     *
63     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
64     */
65    static SafeActivityOptions fromBundle(Bundle bOptions) {
66        return bOptions != null
67                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
68                : null;
69    }
70
71    /**
72     * Constructs a new instance and records {@link Binder#getCallingPid}/
73     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
74     * this object.
75     *
76     * @param options The options to wrap.
77     */
78    SafeActivityOptions(@Nullable ActivityOptions options) {
79        mOriginalCallingPid = Binder.getCallingPid();
80        mOriginalCallingUid = Binder.getCallingUid();
81        mOriginalOptions = options;
82    }
83
84    /**
85     * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
86     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
87     * method.
88     */
89    void setCallerOptions(@Nullable ActivityOptions options) {
90        mRealCallingPid = Binder.getCallingPid();
91        mRealCallingUid = Binder.getCallingUid();
92        mCallerOptions = options;
93    }
94
95    /**
96     * Performs permission check and retrieves the options.
97     *
98     * @param r The record of the being started activity.
99     */
100    ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
101        return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
102    }
103
104    /**
105     * Performs permission check and retrieves the options when options are not being used to launch
106     * a specific activity (i.e. a task is moved to front).
107     */
108    ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
109        return getOptions(null, null, null, supervisor);
110    }
111
112    /**
113     * Performs permission check and retrieves the options.
114     *
115     * @param intent The intent that is being launched.
116     * @param aInfo The info of the activity being launched.
117     * @param callerApp The record of the caller.
118     */
119    ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
120            @Nullable ProcessRecord callerApp,
121            ActivityStackSupervisor supervisor) throws SecurityException {
122        if (mOriginalOptions != null) {
123            checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
124                    mOriginalCallingPid, mOriginalCallingUid);
125            setCallingPidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid);
126        }
127        if (mCallerOptions != null) {
128            checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
129                    mRealCallingPid, mRealCallingUid);
130            setCallingPidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid);
131        }
132        return mergeActivityOptions(mOriginalOptions, mCallerOptions);
133    }
134
135    private void setCallingPidForRemoteAnimationAdapter(ActivityOptions options, int callingPid) {
136        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
137        if (adapter == null) {
138            return;
139        }
140        if (callingPid == Process.myPid()) {
141            Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
142            return;
143        }
144        adapter.setCallingPid(callingPid);
145    }
146
147    /**
148     * @see ActivityOptions#popAppVerificationBundle
149     */
150    Bundle popAppVerificationBundle() {
151        return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
152    }
153
154    private void abort() {
155        if (mOriginalOptions != null) {
156            ActivityOptions.abort(mOriginalOptions);
157        }
158        if (mCallerOptions != null) {
159            ActivityOptions.abort(mCallerOptions);
160        }
161    }
162
163    static void abort(@Nullable SafeActivityOptions options) {
164        if (options != null) {
165            options.abort();
166        }
167    }
168
169    /**
170     * Merges two activity options into one, with {@code options2} taking precedence in case of a
171     * conflict.
172     */
173    @VisibleForTesting
174    @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
175            @Nullable ActivityOptions options2) {
176        if (options1 == null) {
177            return options2;
178        }
179        if (options2 == null) {
180            return options1;
181        }
182        final Bundle b1 = options1.toBundle();
183        final Bundle b2 = options2.toBundle();
184        b1.putAll(b2);
185        return ActivityOptions.fromBundle(b1);
186    }
187
188    private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
189            @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
190            ActivityOptions options, int callingPid, int callingUid) {
191        // If a launch task id is specified, then ensure that the caller is the recents
192        // component or has the START_TASKS_FROM_RECENTS permission
193        if (options.getLaunchTaskId() != INVALID_TASK_ID
194                && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
195            final int startInTaskPerm = supervisor.mService.checkPermission(
196                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
197            if (startInTaskPerm == PERMISSION_DENIED) {
198                final String msg = "Permission Denial: starting " + getIntentString(intent)
199                        + " from " + callerApp + " (pid=" + callingPid
200                        + ", uid=" + callingUid + ") with launchTaskId="
201                        + options.getLaunchTaskId();
202                Slog.w(TAG, msg);
203                throw new SecurityException(msg);
204            }
205        }
206        // Check if someone tries to launch an activity on a private display with a different
207        // owner.
208        final int launchDisplayId = options.getLaunchDisplayId();
209        if (aInfo != null && launchDisplayId != INVALID_DISPLAY
210                && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
211                        launchDisplayId, aInfo)) {
212            final String msg = "Permission Denial: starting " + getIntentString(intent)
213                    + " from " + callerApp + " (pid=" + callingPid
214                    + ", uid=" + callingUid + ") with launchDisplayId="
215                    + launchDisplayId;
216            Slog.w(TAG, msg);
217            throw new SecurityException(msg);
218        }
219        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
220        final boolean lockTaskMode = options.getLockTaskMode();
221        if (aInfo != null && lockTaskMode
222                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
223                        UserHandle.getUserId(callingUid), aInfo.packageName)) {
224            final String msg = "Permission Denial: starting " + getIntentString(intent)
225                    + " from " + callerApp + " (pid=" + callingPid
226                    + ", uid=" + callingUid + ") with lockTaskMode=true";
227            Slog.w(TAG, msg);
228            throw new SecurityException(msg);
229        }
230
231        // Check permission for remote animations
232        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
233        if (adapter != null && supervisor.mService.checkPermission(
234                CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
235                        != PERMISSION_GRANTED) {
236            final String msg = "Permission Denial: starting " + getIntentString(intent)
237                    + " from " + callerApp + " (pid=" + callingPid
238                    + ", uid=" + callingUid + ") with remoteAnimationAdapter";
239            Slog.w(TAG, msg);
240            throw new SecurityException(msg);
241        }
242    }
243
244    private String getIntentString(Intent intent) {
245        return intent != null ? intent.toString() : "(no intent)";
246    }
247}
248