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:
parent
98b57c304d
commit
dbd7bb2e0e
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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, "");
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user