Merge "If the calling process isn't whitelisted for background activity start, check the other processes of calling UID too" into qt-dev

This commit is contained in:
TreeHugger Robot
2019-04-16 09:38:25 +00:00
committed by Android (Google) Code Review
6 changed files with 289 additions and 66 deletions

View File

@@ -986,31 +986,6 @@ class ActivityStarter {
return false;
}
}
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
// caller, so that we can make the decision based on its foreground/whitelisted state.
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
}
if (callerApp != null) {
// don't abort if the callerApp is instrumenting with background activity starts privs
if (callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) {
return false;
}
// don't abort if the caller is currently temporarily whitelisted
if (callerApp.areBackgroundActivityStartsAllowed()) {
return false;
}
// don't abort if the caller has an activity in any foreground task
if (callerApp.hasActivityInVisibleTask()) {
return false;
}
// don't abort if the caller is bound by a UID that's currently foreground
if (isBoundByForegroundUid(callerApp)) {
return false;
}
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
@@ -1035,6 +1010,33 @@ class ActivityStarter {
+ " temporarily whitelisted. This will not be supported in future Q builds.");
return false;
}
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
// caller, so that we can make the decision based on its foreground/whitelisted state.
int callerAppUid = callingUid;
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
// don't abort if the callerApp or other processes of that uid are whitelisted in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
return false;
}
// only if that one wasn't whitelisted, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) {
return false;
}
}
}
}
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
@@ -1059,17 +1061,6 @@ class ActivityStarter {
return true;
}
private boolean isBoundByForegroundUid(WindowProcessController callerApp) {
final ArraySet<Integer> boundClientUids = callerApp.getBoundClientUids();
for (int i = boundClientUids.size() - 1; i >= 0; --i) {
final int uid = boundClientUids.valueAt(i);
if (mService.isUidForeground(uid)) {
return true;
}
}
return false;
}
// TODO: remove this toast after feature development is done
void showBackgroundActivityBlockedToast(boolean abort, String callingPackage) {
final Resources res = mService.mContext.getResources();

View File

@@ -373,8 +373,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
/** All processes currently running that might have a window organized by name. */
final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
/** All processes we currently have running mapped by pid */
final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
/** All processes we currently have running mapped by pid and uid */
final WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();
/** This is the process holding what we currently consider to be the "home" activity. */
WindowProcessController mHomeProcess;
/** The currently running heavy-weight process, if any. */
@@ -913,7 +913,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return getGlobalConfiguration();
}
synchronized (mGlobalLock) {
final WindowProcessController app = mPidMap.get(pid);
final WindowProcessController app = mProcessMap.getProcess(pid);
return app != null ? app.getConfiguration() : getGlobalConfiguration();
}
}
@@ -4640,7 +4640,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
final int pid = Binder.getCallingPid();
final WindowProcessController wpc = mPidMap.get(pid);
final WindowProcessController wpc = mProcessMap.getProcess(pid);
mVrController.setVrThreadLocked(tid, pid, wpc);
}
}
@@ -4659,7 +4659,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
final int pid = Binder.getCallingPid();
final WindowProcessController proc = mPidMap.get(pid);
final WindowProcessController proc = mProcessMap.getProcess(pid);
mVrController.setPersistentVrThreadLocked(tid, pid, proc);
}
}
@@ -5204,9 +5204,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mH.sendMessage(msg);
}
for (int i = mPidMap.size() - 1; i >= 0; i--) {
final int pid = mPidMap.keyAt(i);
final WindowProcessController app = mPidMap.get(pid);
SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final int pid = pidMap.keyAt(i);
final WindowProcessController app = pidMap.get(pid);
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION, "Update process config of "
+ app.mName + " to new config " + configCopy);
@@ -5859,7 +5860,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
WindowProcessController getProcessController(int pid, int uid) {
final WindowProcessController proc = mPidMap.get(pid);
final WindowProcessController proc = mProcessMap.getProcess(pid);
if (proc == null) return null;
if (UserHandle.isApp(uid) && proc.mUid == uid) {
return proc;
@@ -6423,14 +6424,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void onProcessMapped(int pid, WindowProcessController proc) {
synchronized (mGlobalLock) {
mPidMap.put(pid, proc);
mProcessMap.put(pid, proc);
}
}
@Override
public void onProcessUnMapped(int pid) {
synchronized (mGlobalLock) {
mPidMap.remove(pid);
mProcessMap.remove(pid);
}
}
@@ -6503,7 +6504,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return;
}
final WindowProcessController process = mPidMap.get(pid);
final WindowProcessController process = mProcessMap.getProcess(pid);
if (process == null) {
if (DEBUG_CONFIGURATION) {
Slog.w(TAG, "Trying to update display configuration for invalid "
@@ -6696,7 +6697,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// Only allow this from foreground processes, so that background
// applications can't abuse it to prevent system UI from being shown.
if (uid >= FIRST_APPLICATION_UID) {
final WindowProcessController proc = mPidMap.get(pid);
final WindowProcessController proc = mProcessMap.getProcess(pid);
if (!proc.isPerceptible()) {
Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ " from background process " + proc);

View File

@@ -48,6 +48,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
public final class CompatModePackages {
@@ -324,8 +325,9 @@ public final class CompatModePackages {
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
final WindowProcessController app = mService.mPidMap.valueAt(i);
SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final WindowProcessController app = pidMap.valueAt(i);
if (!app.mPkgList.contains(packageName)) {
continue;
}

View File

@@ -372,18 +372,39 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
}
public boolean areBackgroundActivityStartsAllowed() {
return mAllowBackgroundActivityStarts;
boolean areBackgroundActivityStartsAllowed() {
// allow if the whitelisting flag was explicitly set
if (mAllowBackgroundActivityStarts) {
return true;
}
// allow if the proc is instrumenting with background activity starts privs
if (mInstrumentingWithBackgroundActivityStartPrivileges) {
return true;
}
// allow if the caller has an activity in any foreground task
if (hasActivityInVisibleTask()) {
return true;
}
// allow if the caller is bound by a UID that's currently foreground
if (isBoundByForegroundUid()) {
return true;
}
return false;
}
private boolean isBoundByForegroundUid() {
for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) {
return true;
}
}
return false;
}
public void setBoundClientUids(ArraySet<Integer> boundClientUids) {
mBoundClientUids = boundClientUids;
}
public ArraySet<Integer> getBoundClientUids() {
return mBoundClientUids;
}
public void setInstrumenting(boolean instrumenting,
boolean hasBackgroundActivityStartPrivileges) {
mInstrumenting = instrumenting;
@@ -394,14 +415,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mInstrumenting;
}
/**
* @return true if the instrumentation was started by a holder of
* START_ACTIVITIES_FROM_BACKGROUND permission
*/
boolean isInstrumentingWithBackgroundActivityStartPrivileges() {
return mInstrumentingWithBackgroundActivityStartPrivileges;
}
public void setPerceptible(boolean perceptible) {
mPerceptible = perceptible;
}
@@ -487,7 +500,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
boolean hasActivityInVisibleTask() {
private boolean hasActivityInVisibleTask() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
TaskRecord task = mActivities.get(i).getTaskRecord();
if (task == null) {

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.util.ArraySet;
import android.util.SparseArray;
import java.util.Map;
import java.util.HashMap;
final class WindowProcessControllerMap {
/** All processes we currently have running mapped by pid */
private final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
/** All processes we currently have running mapped by uid */
private final Map<Integer, ArraySet<WindowProcessController>> mUidMap = new HashMap<>();
/** Retrieves a currently running process for pid. */
WindowProcessController getProcess(int pid) {
return mPidMap.get(pid);
}
/** Retrieves all currently running processes for uid. */
ArraySet<WindowProcessController> getProcesses(int uid) {
return mUidMap.get(uid);
}
SparseArray<WindowProcessController> getPidMap() {
return mPidMap;
}
void put(int pid, WindowProcessController proc) {
// if there is a process for this pid already in mPidMap it'll get replaced automagically,
// but we actually need to remove it from mUidMap too before adding the new one
final WindowProcessController prevProc = mPidMap.get(pid);
if (prevProc != null) {
removeProcessFromUidMap(prevProc);
}
// put process into mPidMap
mPidMap.put(pid, proc);
// put process into mUidMap
final int uid = proc.mUid;
ArraySet<WindowProcessController> procSet = mUidMap.getOrDefault(uid,
new ArraySet<WindowProcessController>());
procSet.add(proc);
mUidMap.put(uid, procSet);
}
void remove(int pid) {
final WindowProcessController proc = mPidMap.get(pid);
if (proc != null) {
// remove process from mPidMap
mPidMap.remove(pid);
// remove process from mUidMap
removeProcessFromUidMap(proc);
}
}
private void removeProcessFromUidMap(WindowProcessController proc) {
if (proc == null) {
return;
}
final int uid = proc.mUid;
ArraySet<WindowProcessController> procSet = mUidMap.get(uid);
if (procSet != null) {
procSet.remove(proc);
if (procSet.isEmpty()) {
mUidMap.remove(uid);
}
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for the {@link WindowProcessControllerMap} class.
*
* Build/Install/Run:
* atest WmTests:WindowProcessControllerMapTests
*/
@SmallTest
@Presubmit
public class WindowProcessControllerMapTests extends ActivityTestsBase {
private static final int FAKE_UID1 = 666;
private static final int FAKE_UID2 = 667;
private static final int FAKE_PID1 = 668;
private static final int FAKE_PID2 = 669;
private static final int FAKE_PID3 = 670;
private static final int FAKE_PID4 = 671;
private WindowProcessControllerMap mProcessMap;
private WindowProcessController pid1uid1;
private WindowProcessController pid1uid2;
private WindowProcessController pid2uid1;
private WindowProcessController pid3uid1;
private WindowProcessController pid4uid2;
@Before
public void setUp() throws Exception {
mProcessMap = new WindowProcessControllerMap();
pid1uid1 = new WindowProcessController(
mService, mService.mContext.getApplicationInfo(), "fakepid1fakeuid1", FAKE_UID1,
UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class));
pid1uid1.setPid(FAKE_PID1);
pid1uid2 = new WindowProcessController(
mService, mService.mContext.getApplicationInfo(), "fakepid1fakeuid2", FAKE_UID2,
UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class));
pid1uid2.setPid(FAKE_PID1);
pid2uid1 = new WindowProcessController(
mService, mService.mContext.getApplicationInfo(), "fakepid2fakeuid1", FAKE_UID1,
UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class));
pid2uid1.setPid(FAKE_PID2);
pid3uid1 = new WindowProcessController(
mService, mService.mContext.getApplicationInfo(), "fakepid3fakeuid1", FAKE_UID1,
UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class));
pid3uid1.setPid(FAKE_PID3);
pid4uid2 = new WindowProcessController(
mService, mService.mContext.getApplicationInfo(), "fakepid4fakeuid2", FAKE_UID2,
UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class));
pid4uid2.setPid(FAKE_PID4);
}
@Test
public void testAdditionsAndRemovals() {
// test various additions and removals
mProcessMap.put(FAKE_PID1, pid1uid1);
mProcessMap.put(FAKE_PID2, pid2uid1);
assertEquals(pid1uid1, mProcessMap.getProcess(FAKE_PID1));
assertEquals(pid2uid1, mProcessMap.getProcess(FAKE_PID2));
ArraySet<WindowProcessController> uid1processes = mProcessMap.getProcesses(FAKE_UID1);
assertTrue(uid1processes.contains(pid1uid1));
assertTrue(uid1processes.contains(pid2uid1));
assertEquals(uid1processes.size(), 2);
mProcessMap.remove(FAKE_PID2);
mProcessMap.put(FAKE_PID3, pid3uid1);
uid1processes = mProcessMap.getProcesses(FAKE_UID1);
assertTrue(uid1processes.contains(pid1uid1));
assertFalse(uid1processes.contains(pid2uid1));
assertTrue(uid1processes.contains(pid3uid1));
assertEquals(uid1processes.size(), 2);
mProcessMap.put(FAKE_PID4, pid4uid2);
ArraySet<WindowProcessController> uid2processes = mProcessMap.getProcesses(FAKE_UID2);
assertTrue(uid2processes.contains(pid4uid2));
assertEquals(uid2processes.size(), 1);
mProcessMap.remove(FAKE_PID1);
mProcessMap.remove(FAKE_PID3);
assertNull(mProcessMap.getProcesses(FAKE_UID1));
assertEquals(mProcessMap.getProcess(FAKE_PID4), pid4uid2);
}
@Test
public void testReplacement() {
// test that replacing a process is handled correctly
mProcessMap.put(FAKE_PID1, pid1uid1);
ArraySet<WindowProcessController> uid1processes = mProcessMap.getProcesses(FAKE_UID1);
assertTrue(uid1processes.contains(pid1uid1));
assertEquals(uid1processes.size(), 1);
mProcessMap.put(FAKE_PID1, pid1uid2);
assertNull(mProcessMap.getProcesses(FAKE_UID1));
ArraySet<WindowProcessController> uid2processes = mProcessMap.getProcesses(FAKE_UID2);
assertTrue(uid2processes.contains(pid1uid2));
assertEquals(uid2processes.size(), 1);
assertEquals(mProcessMap.getProcess(FAKE_PID1), pid1uid2);
}
}