Loading core/java/android/os/Binder.java +9 −3 Original line number Diff line number Diff line Loading @@ -84,6 +84,15 @@ public class Binder implements IBinder { /** @hide */ public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE /** * Value to represents that a calling work source is not set. * * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource. * * @hide */ public static final int UNSET_WORKSOURCE = -1; /** * Control whether dump() calls are allowed. */ Loading Loading @@ -449,8 +458,6 @@ public class Binder implements IBinder { * } * </pre> * * <p>The work source will be propagated for future outgoing binder transactions * executed on this thread. * @hide **/ @CriticalNative Loading Loading @@ -972,7 +979,6 @@ public class Binder implements IBinder { if (observer != null) { observer.callEnded(callSession, requestSizeBytes, replySizeBytes); } return res; } } core/java/com/android/internal/os/BinderCallsStats.java +3 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.os.Binder; import android.os.Process; import android.os.SystemClock; import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; Loading Loading @@ -162,8 +163,7 @@ public class BinderCallsStats implements BinderInternal.Observer { return; } final boolean isWorkSourceSet = workSourceUid >= 0; final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid); final UidEntry uidEntry = getUidEntry(workSourceUid); uidEntry.callCount++; if (recordCall) { Loading Loading @@ -464,7 +464,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } protected int getWorkSourceUid() { return Binder.getCallingWorkSourceUid(); return ThreadLocalWorkSource.getUid(); } protected long getElapsedRealtimeMicro() { Loading core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +0 −16 Original line number Diff line number Diff line Loading @@ -527,22 +527,6 @@ public class BinderCallsStatsTest { assertEquals(0, stat.exceptionCount); } @Test public void testCallingUidUsedWhenWorkSourceNotSet() { TestBinderCallsStats bcs = new TestBinderCallsStats(); bcs.setDetailedTracking(true); bcs.workSourceUid = -1; Binder binder = new Binder(); CallSession callSession = bcs.callStarted(binder, 1); bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); assertEquals(1, bcs.getExportedCallStats().size()); BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0); assertEquals(CALLING_UID, stat.workSourceUid); assertEquals(CALLING_UID, stat.callingUid); } @Test public void testGetExportedStatsWithoutCalls() { TestBinderCallsStats bcs = new TestBinderCallsStats(); Loading services/core/java/com/android/server/BinderCallsStatsService.java +136 −8 Original line number Diff line number Diff line Loading @@ -16,23 +16,33 @@ package com.android.server; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import android.app.AppGlobals; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.BinderInternal.CallSession; import com.android.internal.os.CachedDeviceState; import java.io.FileDescriptor; Loading @@ -49,6 +59,106 @@ public class BinderCallsStatsService extends Binder { private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; /** Resolves the work source of an incoming binder transaction. */ static class WorkSourceProvider { private ArraySet<Integer> mAppIdWhitelist; WorkSourceProvider() { mAppIdWhitelist = new ArraySet<>(); } public int resolveWorkSourceUid() { final int callingUid = getCallingUid(); final int appId = UserHandle.getAppId(callingUid); if (mAppIdWhitelist.contains(appId)) { final int workSource = getCallingWorkSourceUid(); final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE; return isWorkSourceSet ? workSource : callingUid; } return callingUid; } public void systemReady(Context context) { mAppIdWhitelist = createAppidWhitelist(context); } public void dump(PrintWriter pw, Map<Integer, String> appIdToPackageName) { pw.println("AppIds of apps that can set the work source:"); final ArraySet<Integer> whitelist = mAppIdWhitelist; for (Integer appId : whitelist) { pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(appId))); } } protected int getCallingUid() { return Binder.getCallingUid(); } protected int getCallingWorkSourceUid() { return Binder.getCallingWorkSourceUid(); } private ArraySet<Integer> createAppidWhitelist(Context context) { // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access. final ArraySet<Integer> whitelist = new ArraySet<>(); // We trust our own process. whitelist.add(Process.myUid()); // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission. final PackageManager pm = context.getPackageManager(); final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS }; final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; final List<PackageInfo> packages = pm.getPackagesHoldingPermissions(permissions, queryFlags); final int packagesSize = packages.size(); for (int i = 0; i < packagesSize; i++) { final PackageInfo pkgInfo = packages.get(i); try { final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags); final int appId = UserHandle.getAppId(uid); whitelist.add(appId); } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e); } } return whitelist; } } /** Observer for all system server incoming binder transactions. */ @VisibleForTesting static class BinderCallsObserver implements BinderInternal.Observer { private final BinderInternal.Observer mBinderCallsStats; private final WorkSourceProvider mWorkSourceProvider; BinderCallsObserver(BinderInternal.Observer callsStats, WorkSourceProvider workSourceProvider) { mBinderCallsStats = callsStats; mWorkSourceProvider = workSourceProvider; } @Override public CallSession callStarted(Binder binder, int code) { // We depend on the code in Binder#execTransact to reset the state of // ThreadLocalWorkSource setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid()); return mBinderCallsStats.callStarted(binder, code); } @Override public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize); } @Override public void callThrewException(CallSession s, Exception exception) { mBinderCallsStats.callThrewException(s, exception); } protected void setThreadLocalWorkSourceUid(int uid) { ThreadLocalWorkSource.setUid(uid); } } /** Listens for flag changes. */ private static class SettingsObserver extends ContentObserver { Loading @@ -63,13 +173,16 @@ public class BinderCallsStatsService extends Binder { private final Context mContext; private final KeyValueListParser mParser = new KeyValueListParser(','); private final BinderCallsStats mBinderCallsStats; private final BinderCallsObserver mBinderCallsObserver; public SettingsObserver(Context context, BinderCallsStats binderCallsStats) { SettingsObserver(Context context, BinderCallsStats binderCallsStats, BinderCallsObserver observer) { super(BackgroundThread.getHandler()); mContext = context; context.getContentResolver().registerContentObserver(mUri, false, this, UserHandle.USER_SYSTEM); mBinderCallsStats = binderCallsStats; mBinderCallsObserver = observer; // Always kick once to ensure that we match current state onChange(); } Loading Loading @@ -107,7 +220,7 @@ public class BinderCallsStatsService extends Binder { mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT); if (mEnabled != enabled) { if (enabled) { Binder.setObserver(mBinderCallsStats); Binder.setObserver(mBinderCallsObserver); Binder.setProxyTransactListener( new Binder.PropagateWorkSourceTransactListener()); } else { Loading Loading @@ -155,6 +268,7 @@ public class BinderCallsStatsService extends Binder { public static class LifeCycle extends SystemService { private BinderCallsStatsService mService; private BinderCallsStats mBinderCallsStats; private WorkSourceProvider mWorkSourceProvider; public LifeCycle(Context context) { super(context); Loading @@ -163,7 +277,11 @@ public class BinderCallsStatsService extends Binder { @Override public void onStart() { mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector()); mService = new BinderCallsStatsService(mBinderCallsStats); mWorkSourceProvider = new WorkSourceProvider(); BinderCallsObserver binderCallsObserver = new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider); mService = new BinderCallsStatsService( mBinderCallsStats, binderCallsObserver, mWorkSourceProvider); publishLocalService(Internal.class, new Internal(mBinderCallsStats)); publishBinderService("binder_calls_stats", mService); boolean detailedTrackingEnabled = SystemProperties.getBoolean( Loading @@ -182,21 +300,29 @@ public class BinderCallsStatsService extends Binder { if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) { CachedDeviceState.Readonly deviceState = getLocalService( CachedDeviceState.Readonly.class); mService.systemReady(getContext()); mBinderCallsStats.setDeviceState(deviceState); // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); mService.systemReady(getContext()); } } } private SettingsObserver mSettingsObserver; private final BinderCallsStats mBinderCallsStats; private final BinderCallsObserver mBinderCallsObserver; private final WorkSourceProvider mWorkSourceProvider; BinderCallsStatsService(BinderCallsStats binderCallsStats) { BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer, WorkSourceProvider workSourceProvider) { mBinderCallsStats = binderCallsStats; mBinderCallsObserver = observer; mWorkSourceProvider = workSourceProvider; } public void systemReady(Context context) { mSettingsObserver = new SettingsObserver(context, mBinderCallsStats); mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver); } public void reset() { Loading @@ -216,7 +342,7 @@ public class BinderCallsStatsService extends Binder { pw.println("binder_calls_stats reset."); return; } else if ("--enable".equals(arg)) { Binder.setObserver(mBinderCallsStats); Binder.setObserver(mBinderCallsObserver); return; } else if ("--disable".equals(arg)) { Binder.setObserver(null); Loading @@ -234,6 +360,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setDetailedTracking(false); pw.println("Detailed tracking disabled"); return; } else if ("--dump-worksource-provider".equals(arg)) { mWorkSourceProvider.dump(pw, getAppIdToPackagesMap()); return; } else if ("-h".equals(arg)) { pw.println("binder_calls_stats commands:"); pw.println(" --reset: Reset stats"); Loading Loading @@ -272,5 +401,4 @@ public class BinderCallsStatsService extends Binder { } return map; } } services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import static org.junit.Assert.assertEquals; import android.os.Binder; import android.os.Process; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderInternal; import com.android.internal.os.BinderInternal.CallSession; import com.android.server.BinderCallsStatsService.WorkSourceProvider; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) @Presubmit public class BinderCallsStatsServiceTest { @Test public void weTrustOurselves() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { return Process.myUid(); } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(1, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceSetIfCallerHasPermission() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { // System process uid which as UPDATE_DEVICE_STATS. return 1001; } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(1, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceResolvedToCallingUid() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { // UID without permissions. return Integer.MAX_VALUE; } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceSet() { TestObserver observer = new TestObserver(); observer.callStarted(new Binder(), 0); assertEquals(true, observer.workSourceSet); } static class TestObserver extends BinderCallsStatsService.BinderCallsObserver { public boolean workSourceSet = false; TestObserver() { super(new NoopObserver(), new WorkSourceProvider()); } @Override protected void setThreadLocalWorkSourceUid(int uid) { workSourceSet = true; } } static class NoopObserver implements BinderInternal.Observer { @Override public CallSession callStarted(Binder binder, int code) { return null; } @Override public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { } @Override public void callThrewException(CallSession s, Exception exception) { } } } Loading
core/java/android/os/Binder.java +9 −3 Original line number Diff line number Diff line Loading @@ -84,6 +84,15 @@ public class Binder implements IBinder { /** @hide */ public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE /** * Value to represents that a calling work source is not set. * * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource. * * @hide */ public static final int UNSET_WORKSOURCE = -1; /** * Control whether dump() calls are allowed. */ Loading Loading @@ -449,8 +458,6 @@ public class Binder implements IBinder { * } * </pre> * * <p>The work source will be propagated for future outgoing binder transactions * executed on this thread. * @hide **/ @CriticalNative Loading Loading @@ -972,7 +979,6 @@ public class Binder implements IBinder { if (observer != null) { observer.callEnded(callSession, requestSizeBytes, replySizeBytes); } return res; } }
core/java/com/android/internal/os/BinderCallsStats.java +3 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.os.Binder; import android.os.Process; import android.os.SystemClock; import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; Loading Loading @@ -162,8 +163,7 @@ public class BinderCallsStats implements BinderInternal.Observer { return; } final boolean isWorkSourceSet = workSourceUid >= 0; final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid); final UidEntry uidEntry = getUidEntry(workSourceUid); uidEntry.callCount++; if (recordCall) { Loading Loading @@ -464,7 +464,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } protected int getWorkSourceUid() { return Binder.getCallingWorkSourceUid(); return ThreadLocalWorkSource.getUid(); } protected long getElapsedRealtimeMicro() { Loading
core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +0 −16 Original line number Diff line number Diff line Loading @@ -527,22 +527,6 @@ public class BinderCallsStatsTest { assertEquals(0, stat.exceptionCount); } @Test public void testCallingUidUsedWhenWorkSourceNotSet() { TestBinderCallsStats bcs = new TestBinderCallsStats(); bcs.setDetailedTracking(true); bcs.workSourceUid = -1; Binder binder = new Binder(); CallSession callSession = bcs.callStarted(binder, 1); bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); assertEquals(1, bcs.getExportedCallStats().size()); BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0); assertEquals(CALLING_UID, stat.workSourceUid); assertEquals(CALLING_UID, stat.callingUid); } @Test public void testGetExportedStatsWithoutCalls() { TestBinderCallsStats bcs = new TestBinderCallsStats(); Loading
services/core/java/com/android/server/BinderCallsStatsService.java +136 −8 Original line number Diff line number Diff line Loading @@ -16,23 +16,33 @@ package com.android.server; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import android.app.AppGlobals; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.ThreadLocalWorkSource; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.BinderInternal.CallSession; import com.android.internal.os.CachedDeviceState; import java.io.FileDescriptor; Loading @@ -49,6 +59,106 @@ public class BinderCallsStatsService extends Binder { private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; /** Resolves the work source of an incoming binder transaction. */ static class WorkSourceProvider { private ArraySet<Integer> mAppIdWhitelist; WorkSourceProvider() { mAppIdWhitelist = new ArraySet<>(); } public int resolveWorkSourceUid() { final int callingUid = getCallingUid(); final int appId = UserHandle.getAppId(callingUid); if (mAppIdWhitelist.contains(appId)) { final int workSource = getCallingWorkSourceUid(); final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE; return isWorkSourceSet ? workSource : callingUid; } return callingUid; } public void systemReady(Context context) { mAppIdWhitelist = createAppidWhitelist(context); } public void dump(PrintWriter pw, Map<Integer, String> appIdToPackageName) { pw.println("AppIds of apps that can set the work source:"); final ArraySet<Integer> whitelist = mAppIdWhitelist; for (Integer appId : whitelist) { pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(appId))); } } protected int getCallingUid() { return Binder.getCallingUid(); } protected int getCallingWorkSourceUid() { return Binder.getCallingWorkSourceUid(); } private ArraySet<Integer> createAppidWhitelist(Context context) { // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access. final ArraySet<Integer> whitelist = new ArraySet<>(); // We trust our own process. whitelist.add(Process.myUid()); // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission. final PackageManager pm = context.getPackageManager(); final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS }; final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; final List<PackageInfo> packages = pm.getPackagesHoldingPermissions(permissions, queryFlags); final int packagesSize = packages.size(); for (int i = 0; i < packagesSize; i++) { final PackageInfo pkgInfo = packages.get(i); try { final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags); final int appId = UserHandle.getAppId(uid); whitelist.add(appId); } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e); } } return whitelist; } } /** Observer for all system server incoming binder transactions. */ @VisibleForTesting static class BinderCallsObserver implements BinderInternal.Observer { private final BinderInternal.Observer mBinderCallsStats; private final WorkSourceProvider mWorkSourceProvider; BinderCallsObserver(BinderInternal.Observer callsStats, WorkSourceProvider workSourceProvider) { mBinderCallsStats = callsStats; mWorkSourceProvider = workSourceProvider; } @Override public CallSession callStarted(Binder binder, int code) { // We depend on the code in Binder#execTransact to reset the state of // ThreadLocalWorkSource setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid()); return mBinderCallsStats.callStarted(binder, code); } @Override public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize); } @Override public void callThrewException(CallSession s, Exception exception) { mBinderCallsStats.callThrewException(s, exception); } protected void setThreadLocalWorkSourceUid(int uid) { ThreadLocalWorkSource.setUid(uid); } } /** Listens for flag changes. */ private static class SettingsObserver extends ContentObserver { Loading @@ -63,13 +173,16 @@ public class BinderCallsStatsService extends Binder { private final Context mContext; private final KeyValueListParser mParser = new KeyValueListParser(','); private final BinderCallsStats mBinderCallsStats; private final BinderCallsObserver mBinderCallsObserver; public SettingsObserver(Context context, BinderCallsStats binderCallsStats) { SettingsObserver(Context context, BinderCallsStats binderCallsStats, BinderCallsObserver observer) { super(BackgroundThread.getHandler()); mContext = context; context.getContentResolver().registerContentObserver(mUri, false, this, UserHandle.USER_SYSTEM); mBinderCallsStats = binderCallsStats; mBinderCallsObserver = observer; // Always kick once to ensure that we match current state onChange(); } Loading Loading @@ -107,7 +220,7 @@ public class BinderCallsStatsService extends Binder { mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT); if (mEnabled != enabled) { if (enabled) { Binder.setObserver(mBinderCallsStats); Binder.setObserver(mBinderCallsObserver); Binder.setProxyTransactListener( new Binder.PropagateWorkSourceTransactListener()); } else { Loading Loading @@ -155,6 +268,7 @@ public class BinderCallsStatsService extends Binder { public static class LifeCycle extends SystemService { private BinderCallsStatsService mService; private BinderCallsStats mBinderCallsStats; private WorkSourceProvider mWorkSourceProvider; public LifeCycle(Context context) { super(context); Loading @@ -163,7 +277,11 @@ public class BinderCallsStatsService extends Binder { @Override public void onStart() { mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector()); mService = new BinderCallsStatsService(mBinderCallsStats); mWorkSourceProvider = new WorkSourceProvider(); BinderCallsObserver binderCallsObserver = new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider); mService = new BinderCallsStatsService( mBinderCallsStats, binderCallsObserver, mWorkSourceProvider); publishLocalService(Internal.class, new Internal(mBinderCallsStats)); publishBinderService("binder_calls_stats", mService); boolean detailedTrackingEnabled = SystemProperties.getBoolean( Loading @@ -182,21 +300,29 @@ public class BinderCallsStatsService extends Binder { if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) { CachedDeviceState.Readonly deviceState = getLocalService( CachedDeviceState.Readonly.class); mService.systemReady(getContext()); mBinderCallsStats.setDeviceState(deviceState); // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); mService.systemReady(getContext()); } } } private SettingsObserver mSettingsObserver; private final BinderCallsStats mBinderCallsStats; private final BinderCallsObserver mBinderCallsObserver; private final WorkSourceProvider mWorkSourceProvider; BinderCallsStatsService(BinderCallsStats binderCallsStats) { BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer, WorkSourceProvider workSourceProvider) { mBinderCallsStats = binderCallsStats; mBinderCallsObserver = observer; mWorkSourceProvider = workSourceProvider; } public void systemReady(Context context) { mSettingsObserver = new SettingsObserver(context, mBinderCallsStats); mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver); } public void reset() { Loading @@ -216,7 +342,7 @@ public class BinderCallsStatsService extends Binder { pw.println("binder_calls_stats reset."); return; } else if ("--enable".equals(arg)) { Binder.setObserver(mBinderCallsStats); Binder.setObserver(mBinderCallsObserver); return; } else if ("--disable".equals(arg)) { Binder.setObserver(null); Loading @@ -234,6 +360,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setDetailedTracking(false); pw.println("Detailed tracking disabled"); return; } else if ("--dump-worksource-provider".equals(arg)) { mWorkSourceProvider.dump(pw, getAppIdToPackagesMap()); return; } else if ("-h".equals(arg)) { pw.println("binder_calls_stats commands:"); pw.println(" --reset: Reset stats"); Loading Loading @@ -272,5 +401,4 @@ public class BinderCallsStatsService extends Binder { } return map; } }
services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import static org.junit.Assert.assertEquals; import android.os.Binder; import android.os.Process; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderInternal; import com.android.internal.os.BinderInternal.CallSession; import com.android.server.BinderCallsStatsService.WorkSourceProvider; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) @Presubmit public class BinderCallsStatsServiceTest { @Test public void weTrustOurselves() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { return Process.myUid(); } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(1, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceSetIfCallerHasPermission() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { // System process uid which as UPDATE_DEVICE_STATS. return 1001; } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(1, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceResolvedToCallingUid() { WorkSourceProvider workSourceProvider = new WorkSourceProvider() { protected int getCallingUid() { // UID without permissions. return Integer.MAX_VALUE; } protected int getCallingWorkSourceUid() { return 1; } }; workSourceProvider.systemReady(InstrumentationRegistry.getContext()); assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid()); } @Test public void workSourceSet() { TestObserver observer = new TestObserver(); observer.callStarted(new Binder(), 0); assertEquals(true, observer.workSourceSet); } static class TestObserver extends BinderCallsStatsService.BinderCallsObserver { public boolean workSourceSet = false; TestObserver() { super(new NoopObserver(), new WorkSourceProvider()); } @Override protected void setThreadLocalWorkSourceUid(int uid) { workSourceSet = true; } } static class NoopObserver implements BinderInternal.Observer { @Override public CallSession callStarted(Binder binder, int code) { return null; } @Override public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { } @Override public void callThrewException(CallSession s, Exception exception) { } } }