1/*
2 * Copyright (C) 2017 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 com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
20import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
21
22import android.app.ActivityManager;
23import android.app.ActivityOptions;
24import android.app.IAppTask;
25import android.app.IApplicationThread;
26import android.content.Intent;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.IBinder;
30import android.os.UserHandle;
31
32/**
33 * An implementation of IAppTask, that allows an app to manage its own tasks via
34 * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
35 * only the process that calls getAppTasks() can call the AppTask methods.
36 */
37class AppTaskImpl extends IAppTask.Stub {
38    private ActivityManagerService mService;
39
40    private int mTaskId;
41    private int mCallingUid;
42
43    public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) {
44        mService = service;
45        mTaskId = taskId;
46        mCallingUid = callingUid;
47    }
48
49    private void checkCaller() {
50        if (mCallingUid != Binder.getCallingUid()) {
51            throw new SecurityException("Caller " + mCallingUid
52                    + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
53        }
54    }
55
56    @Override
57    public void finishAndRemoveTask() {
58        checkCaller();
59
60        synchronized (mService) {
61            long origId = Binder.clearCallingIdentity();
62            try {
63                // We remove the task from recents to preserve backwards
64                if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
65                        REMOVE_FROM_RECENTS, "finish-and-remove-task")) {
66                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
67                }
68            } finally {
69                Binder.restoreCallingIdentity(origId);
70            }
71        }
72    }
73
74    @Override
75    public ActivityManager.RecentTaskInfo getTaskInfo() {
76        checkCaller();
77
78        synchronized (mService) {
79            long origId = Binder.clearCallingIdentity();
80            try {
81                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
82                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
83                if (tr == null) {
84                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
85                }
86                return mService.getRecentTasks().createRecentTaskInfo(tr);
87            } finally {
88                Binder.restoreCallingIdentity(origId);
89            }
90        }
91    }
92
93    @Override
94    public void moveToFront() {
95        checkCaller();
96        // Will bring task to front if it already has a root activity.
97        final int callingPid = Binder.getCallingPid();
98        final int callingUid = Binder.getCallingUid();
99        final long origId = Binder.clearCallingIdentity();
100        try {
101            synchronized (mService) {
102                mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
103                        null);
104            }
105        } finally {
106            Binder.restoreCallingIdentity(origId);
107        }
108    }
109
110    @Override
111    public int startActivity(IBinder whoThread, String callingPackage,
112            Intent intent, String resolvedType, Bundle bOptions) {
113        checkCaller();
114
115        int callingUser = UserHandle.getCallingUserId();
116        TaskRecord tr;
117        IApplicationThread appThread;
118        synchronized (mService) {
119            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
120                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
121            if (tr == null) {
122                throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
123            }
124            appThread = IApplicationThread.Stub.asInterface(whoThread);
125            if (appThread == null) {
126                throw new IllegalArgumentException("Bad app thread " + appThread);
127            }
128        }
129
130        return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
131                .setCaller(appThread)
132                .setCallingPackage(callingPackage)
133                .setResolvedType(resolvedType)
134                .setActivityOptions(bOptions)
135                .setMayWait(callingUser)
136                .setInTask(tr)
137                .execute();
138    }
139
140    @Override
141    public void setExcludeFromRecents(boolean exclude) {
142        checkCaller();
143
144        synchronized (mService) {
145            long origId = Binder.clearCallingIdentity();
146            try {
147                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
148                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
149                if (tr == null) {
150                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
151                }
152                Intent intent = tr.getBaseIntent();
153                if (exclude) {
154                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
155                } else {
156                    intent.setFlags(intent.getFlags()
157                            & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
158                }
159            } finally {
160                Binder.restoreCallingIdentity(origId);
161            }
162        }
163    }
164}
165