Home | History | Annotate | Download | only in am
      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 
     17 package com.android.server.am;
     18 
     19 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
     20 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
     21 import static android.content.pm.PackageManager.PERMISSION_DENIED;
     22 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
     23 import static android.view.Display.INVALID_DISPLAY;
     24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
     25 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
     26 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
     27 
     28 import android.annotation.Nullable;
     29 import android.app.ActivityOptions;
     30 import android.app.PendingIntent;
     31 import android.content.Intent;
     32 import android.content.pm.ActivityInfo;
     33 import android.os.Binder;
     34 import android.os.Bundle;
     35 import android.os.Process;
     36 import android.os.UserHandle;
     37 import android.util.Slog;
     38 import android.view.RemoteAnimationAdapter;
     39 
     40 import 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  */
     47 class 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