Provide a system_server internal API to get isolated PIDs of an UID

Internal services within system_server can query the PID list of
the isolated processes with packages matching the given UID.

Also added a shell command to help the testing.

Bug: 191703385
Test: atest FrameworksServicesTests:ActivityManagerTest
Change-Id: I9749bc9c70902221f41945c7e785d13631acbae2
This commit is contained in:
Jing Ji 2021-06-24 20:18:39 -07:00
parent 98b57c304d
commit dbd7bb2e0e
7 changed files with 244 additions and 0 deletions

View File

@ -640,4 +640,10 @@ public abstract class ActivityManagerInternal {
* Returns the capability of the given uid
*/
public abstract @ProcessCapability int getUidCapability(int uid);
/**
* @return The PID list of the isolated process with packages matching the given uid.
*/
@Nullable
public abstract List<Integer> getIsolatedProcesses(int uid);
}

View File

@ -16341,6 +16341,16 @@ public class ActivityManagerService extends IActivityManager.Stub
return uidRecord.getCurCapability();
}
}
/**
* @return The PID list of the isolated process with packages matching the given uid.
*/
@Nullable
public List<Integer> getIsolatedProcesses(int uid) {
synchronized (ActivityManagerService.this) {
return mProcessList.getIsolatedProcessesLocked(uid);
}
}
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {

View File

@ -321,6 +321,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runMemoryFactor(pw);
case "service-restart-backoff":
return runServiceRestartBackoff(pw);
case "get-isolated-pids":
return runGetIsolatedProcesses(pw);
default:
return handleDefaultCommands(cmd);
}
@ -3137,6 +3139,24 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
}
private int runGetIsolatedProcesses(PrintWriter pw) throws RemoteException {
mInternal.enforceCallingPermission(android.Manifest.permission.DUMP,
"getIsolatedProcesses()");
final List<Integer> result = mInternal.mInternal.getIsolatedProcesses(
Integer.parseInt(getNextArgRequired()));
pw.print("[");
if (result != null) {
for (int i = 0, size = result.size(); i < size; i++) {
if (i > 0) {
pw.print(", ");
}
pw.print(result.get(i));
}
}
pw.println("]");
return 0;
}
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@ -3467,6 +3487,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Toggles the restart backoff policy on/off for <PACKAGE_NAME>.");
pw.println(" show <PACKAGE_NAME>");
pw.println(" Shows the restart backoff policy state for <PACKAGE_NAME>.");
pw.println(" get-isolated-pids <UID>");
pw.println(" Get the PIDs of isolated processes with packages in this <UID>");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}

View File

@ -56,6 +56,7 @@ import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityThread;
@ -2988,6 +2989,22 @@ public final class ProcessList {
}
}
@Nullable
@GuardedBy("mService")
List<Integer> getIsolatedProcessesLocked(int uid) {
List<Integer> ret = null;
for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
final ProcessRecord app = mIsolatedProcesses.valueAt(i);
if (app.info.uid == uid) {
if (ret == null) {
ret = new ArrayList<>();
}
ret.add(app.getPid());
}
}
return ret;
}
@GuardedBy("mService")
ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, HostingRecord hostingRecord) {

View File

@ -35,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
@ -43,7 +44,9 @@ import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@ -53,6 +56,7 @@ import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@ -62,11 +66,13 @@ import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Tests for {@link ActivityManager}.
@ -105,6 +111,9 @@ public class ActivityManagerTest {
private static final int COMMAND_UNBIND_SERVICE = 3;
private static final int COMMAND_STOP_SELF = 4;
private static final String TEST_ISOLATED_CLASS =
"com.android.servicestests.apps.simpleservicetestapp.SimpleIsolatedService";
private IActivityManager mService;
private IRemoteCallback mCallback;
private Context mContext;
@ -568,6 +577,127 @@ public class ActivityManagerTest {
return -1;
}
@Test
public void testGetIsolatedProcesses() throws Exception {
final ActivityManager am = mContext.getSystemService(ActivityManager.class);
final PackageManager pm = mContext.getPackageManager();
final int uid1 = pm.getPackageUid(TEST_APP1, 0);
final int uid2 = pm.getPackageUid(TEST_APP2, 0);
final int uid3 = pm.getPackageUid(TEST_APP3, 0);
final List<Pair<Integer, ServiceConnection>> uid1Processes = new ArrayList<>();
final List<Pair<Integer, ServiceConnection>> uid2Processes = new ArrayList<>();
try {
assertTrue("There shouldn't be any isolated process for " + TEST_APP1,
getIsolatedProcesses(uid1).isEmpty());
assertTrue("There shouldn't be any isolated process for " + TEST_APP2,
getIsolatedProcesses(uid2).isEmpty());
assertTrue("There shouldn't be any isolated process for " + TEST_APP3,
getIsolatedProcesses(uid3).isEmpty());
// Verify uid1
uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
// Let one of the processes go
final Pair<Integer, ServiceConnection> uid1P2 = uid1Processes.remove(2);
mContext.unbindService(uid1P2.second);
Thread.sleep(5_000); // Wait for the process gone.
verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
// Verify uid2
uid2Processes.add(createIsolatedProcessAndVerify(TEST_APP2, uid2));
verifyIsolatedProcesses(uid2Processes, getIsolatedProcesses(uid2));
// Verify uid1 again
verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
// Verify uid3
assertTrue("There shouldn't be any isolated process for " + TEST_APP3,
getIsolatedProcesses(uid3).isEmpty());
} finally {
for (Pair<Integer, ServiceConnection> p: uid1Processes) {
mContext.unbindService(p.second);
}
for (Pair<Integer, ServiceConnection> p: uid2Processes) {
mContext.unbindService(p.second);
}
am.forceStopPackage(TEST_APP1);
am.forceStopPackage(TEST_APP2);
am.forceStopPackage(TEST_APP3);
}
}
private static List<Integer> getIsolatedProcesses(int uid) throws Exception {
final String output = runShellCommand("am get-isolated-pids " + uid);
final Matcher matcher = Pattern.compile("(\\d+)").matcher(output);
final List<Integer> pids = new ArrayList<>();
while (matcher.find()) {
pids.add(Integer.parseInt(output.substring(matcher.start(), matcher.end())));
}
return pids;
}
private void verifyIsolatedProcesses(List<Pair<Integer, ServiceConnection>> processes,
List<Integer> pids) {
final List<Integer> l = processes.stream().map(p -> p.first).collect(Collectors.toList());
assertTrue("Isolated processes don't match", l.containsAll(pids));
assertTrue("Isolated processes don't match", pids.containsAll(l));
}
private Pair<Integer, ServiceConnection> createIsolatedProcessAndVerify(String pkgName, int uid)
throws Exception {
final Pair<Integer, ServiceConnection> p = createIsolatedProcess(pkgName);
final List<Integer> pids = getIsolatedProcesses(uid);
assertTrue("Can't find the isolated pid " + p.first + " for " + pkgName,
pids.contains(p.first));
return p;
}
private Pair<Integer, ServiceConnection> createIsolatedProcess(String pkgName)
throws Exception {
final int[] pid = new int[1];
final CountDownLatch[] latch = new CountDownLatch[1];
final ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
final IRemoteCallback s = IRemoteCallback.Stub.asInterface(service);
final IBinder callback = new Binder() {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == Binder.FIRST_CALL_TRANSACTION) {
pid[0] = data.readInt();
latch[0].countDown();
return true;
}
return super.onTransact(code, data, reply, flags);
}
};
try {
final Bundle extra = new Bundle();
extra.putBinder(EXTRA_CALLBACK, callback);
s.sendResult(extra);
} catch (RemoteException e) {
fail("Unable to call into isolated process");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
final Intent intent = new Intent();
intent.setClassName(pkgName, TEST_ISOLATED_CLASS);
latch[0] = new CountDownLatch(1);
assertTrue("Unable to create isolated process in " + pkgName,
mContext.bindIsolatedService(intent, Context.BIND_AUTO_CREATE,
Long.toString(SystemClock.uptimeMillis()), mContext.getMainExecutor(), conn));
assertTrue("Timeout to bind to service " + intent.getComponent(),
latch[0].await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
return Pair.create(pid[0], conn);
}
/**
* Make sure the screen state.
*/

View File

@ -24,6 +24,9 @@
android:exported="true" />
<service android:name=".SimpleFgService"
android:exported="true" />
<service android:name=".SimpleIsolatedService"
android:isolatedProcess="true"
android:exported="true" />
</application>
</manifest>

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2021 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.servicestests.apps.simpleservicetestapp;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
public class SimpleIsolatedService extends Service {
private static final String TAG = "SimpleIsolatedService";
private static final String EXTRA_CALLBACK = "callback";
private final IRemoteCallback.Stub mBinder = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle bundle) {
final IBinder callback = bundle.getBinder(EXTRA_CALLBACK);
final Parcel data = Parcel.obtain();
final Parcel reply = Parcel.obtain();
try {
data.writeInt(Process.myPid());
callback.transact(Binder.FIRST_CALL_TRANSACTION, data, reply, 0);
} catch (RemoteException e) {
Log.e(TAG, "Exception", e);
} finally {
data.recycle();
reply.recycle();
}
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mBinder;
}
}