diff options
7 files changed, 303 insertions, 1 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index b36b664d6a93..dbcdecc168c7 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -234,4 +234,11 @@ public abstract class ActivityManagerInternal { * @see android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY */ public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi); + + /** + * Called after the network policy rules are updated by + * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and + * {@param procStateSeq}. + */ + public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq); } diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 88e0d0393450..8ed95ee3ef6b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -63,6 +63,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false; static final boolean DEBUG_LRU = DEBUG_ALL || false; static final boolean DEBUG_MU = DEBUG_ALL || false; + static final boolean DEBUG_NETWORK = DEBUG_ALL || false; static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false; static final boolean DEBUG_PAUSE = DEBUG_ALL || false; static final boolean DEBUG_POWER = DEBUG_ALL || false; @@ -107,6 +108,7 @@ class ActivityManagerDebugConfig { static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : ""; static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : ""; static final String POSTFIX_MU = "_MU"; + static final String POSTFIX_NETWORK = "_Network"; static final String POSTFIX_OOM_ADJ = (APPEND_CATEGORY_NAME) ? "_OomAdj" : ""; static final String POSTFIX_PAUSE = (APPEND_CATEGORY_NAME) ? "_Pause" : ""; static final String POSTFIX_POWER = (APPEND_CATEGORY_NAME) ? "_Power" : ""; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 38b0a30085d6..c9021ad71e50 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -78,6 +78,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; @@ -107,6 +108,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREE import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES; @@ -422,6 +424,7 @@ public class ActivityManagerService extends IActivityManager.Stub private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; private static final String TAG_LRU = TAG + POSTFIX_LRU; private static final String TAG_MU = TAG + POSTFIX_MU; + private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK; private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ; private static final String TAG_POWER = TAG + POSTFIX_POWER; private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS; @@ -23029,7 +23032,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private final class LocalService extends ActivityManagerInternal { + @VisibleForTesting + final class LocalService extends ActivityManagerInternal { @Override public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent, int targetUserId) { @@ -23279,6 +23283,30 @@ public class ActivityManagerService extends IActivityManager.Stub updateOomAdjLocked(pr); } } + + /** + * Called after the network policy rules are updated by + * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and + * {@param procStateSeq}. + */ + @Override + public void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq) { + if (DEBUG_NETWORK) { + Slog.d(TAG_NETWORK, "Got update from NPMS for uid: " + + uid + " seq: " + procStateSeq); + } + synchronized (ActivityManagerService.this) { + final UidRecord record = mActiveUids.get(uid); + if (record == null) { + if (DEBUG_NETWORK) { + Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid + + " procStateSeq: " + procStateSeq); + } + return; + } + record.lastNetworkUpdatedProcStateSeq = procStateSeq; + } + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index cf6c1e132780..e002e9752689 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -40,6 +40,11 @@ public final class UidRecord { * when {@link #curProcState} changes from background to foreground or vice versa. */ long curProcStateSeq; + /** + * Last seq number for which NetworkPolicyManagerService notified ActivityManagerService that + * network policies rules were updated. + */ + long lastNetworkUpdatedProcStateSeq; static final int CHANGE_PROCSTATE = 0; static final int CHANGE_GONE = 1; @@ -92,6 +97,8 @@ public final class UidRecord { sb.append(numProcs); sb.append(" curProcStateSeq:"); sb.append(curProcStateSeq); + sb.append(" lastNetworkUpdatedProcStateSeq:"); + sb.append(lastNetworkUpdatedProcStateSeq); sb.append("}"); return sb.toString(); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 381f37028e7a..8a4f3f76c0ab 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -96,6 +96,7 @@ import android.Manifest; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; @@ -251,6 +252,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int VERSION_SWITCH_UID = 10; private static final int VERSION_LATEST = VERSION_SWITCH_UID; + /** + * Max items written to {@link #ProcStateSeqHistory}. + */ + @VisibleForTesting + public static final int MAX_PROC_STATE_SEQ_HISTORY = + ActivityManager.isLowRamDeviceStatic() ? 50 : 200; + @VisibleForTesting public static final int TYPE_WARNING = 0x1; @VisibleForTesting @@ -412,6 +420,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final IPackageManager mIPm; + private ActivityManagerInternal mActivityManagerInternal; + + /** + * This is used for debugging purposes. Whenever the IUidObserver.onUidStateChanged is called, + * the uid and procStateSeq will be written to this and will be printed as part of dump. + */ + @VisibleForTesting + public ProcStateSeqHistory mObservedHistory + = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY); // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. @@ -628,6 +645,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); try { mActivityManager.registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE, @@ -724,7 +742,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged"); try { synchronized (mUidRulesFirstLock) { + // We received a uid state change callback, add it to the history so that it + // will be useful for debugging. + mObservedHistory.addProcStateSeqUL(uid, procStateSeq); + // Now update the network policy rules as per the updated uid state. updateUidStateUL(uid, procState); + // Updating the network rules is done, so notify AMS about this. + mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -2429,6 +2453,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.println(); } fout.decreaseIndent(); + + fout.println("Observed uid state changes:"); + fout.increaseIndent(); + mObservedHistory.dumpUL(fout); + fout.decreaseIndent(); } } } @@ -3669,4 +3698,74 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + + /** + * This class is used for storing and dumping the last {@link #MAX_PROC_STATE_SEQ_HISTORY} + * (uid, procStateSeq) pairs. + */ + @VisibleForTesting + public static final class ProcStateSeqHistory { + private static final int INVALID_UID = -1; + + /** + * Denotes maximum number of items this history can hold. + */ + private final int mMaxCapacity; + /** + * Used for storing the uid information. + */ + private final int[] mUids; + /** + * Used for storing the sequence numbers associated with {@link #mUids}. + */ + private final long[] mProcStateSeqs; + /** + * Points to the next available slot for writing (uid, procStateSeq) pair. + */ + private int mHistoryNext; + + public ProcStateSeqHistory(int maxCapacity) { + mMaxCapacity = maxCapacity; + mUids = new int[mMaxCapacity]; + Arrays.fill(mUids, INVALID_UID); + mProcStateSeqs = new long[mMaxCapacity]; + } + + @GuardedBy("mUidRulesFirstLock") + public void addProcStateSeqUL(int uid, long procStateSeq) { + mUids[mHistoryNext] = uid; + mProcStateSeqs[mHistoryNext] = procStateSeq; + mHistoryNext = increaseNext(mHistoryNext, 1); + } + + @GuardedBy("mUidRulesFirstLock") + public void dumpUL(IndentingPrintWriter fout) { + if (mUids[0] == INVALID_UID) { + fout.println("NONE"); + return; + } + int index = mHistoryNext; + do { + index = increaseNext(index, -1); + if (mUids[index] == INVALID_UID) { + break; + } + fout.println(getString(mUids[index], mProcStateSeqs[index])); + } while (index != mHistoryNext); + } + + public static String getString(int uid, long procStateSeq) { + return "UID=" + uid + " procStateSeq=" + procStateSeq; + } + + private int increaseNext(int next, int increment) { + next += increment; + if (next >= mMaxCapacity) { + next = 0; + } else if (next < 0) { + next = mMaxCapacity - 1; + } + return next; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index a9b2ae59cef7..f8d105e5ac31 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -32,6 +32,8 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.Time.TIMEZONE_UTC; +import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY; +import static com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; @@ -58,6 +60,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.IUidObserver; @@ -95,6 +98,7 @@ import android.text.format.Time; import android.util.Log; import android.util.TrustedTime; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; import com.android.server.net.NetworkPolicyManagerInternal; @@ -120,10 +124,12 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; +import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -160,6 +166,8 @@ public class NetworkPolicyManagerServiceTest { private static final String TEST_IFACE = "test0"; private static final String TEST_SSID = "AndroidAP"; + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID); /** @@ -186,6 +194,8 @@ public class NetworkPolicyManagerServiceTest { private @Mock PackageManager mPackageManager; private @Mock IPackageManager mIpm; + private static ActivityManagerInternal mActivityManagerInternal; + private IUidObserver mUidObserver; private INetworkManagementEventObserver mNetworkObserver; @@ -222,6 +232,7 @@ public class NetworkPolicyManagerServiceTest { final UsageStatsManagerInternal usageStats = addLocalServiceMock(UsageStatsManagerInternal.class); when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{}); + mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class); } @Before @@ -961,6 +972,75 @@ public class NetworkPolicyManagerServiceTest { } } + @Test + public void testOnUidStateChanged_notifyAMS() throws Exception { + final long procStateSeq = 222; + mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, + procStateSeq); + verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq); + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final IndentingPrintWriter writer = new IndentingPrintWriter( + new PrintWriter(outputStream), " "); + mService.mObservedHistory.dumpUL(writer); + writer.flush(); + assertEquals(ProcStateSeqHistory.getString(UID_A, procStateSeq), + outputStream.toString().trim()); + } + + @Test + public void testProcStateHistory() { + // Verify dump works correctly with no elements added. + verifyProcStateHistoryDump(0); + + // Add items upto half of the max capacity and verify that dump works correctly. + verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY / 2); + + // Add items upto the max capacity and verify that dump works correctly. + verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY); + + // Add more items than max capacity and verify that dump works correctly. + verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY + MAX_PROC_STATE_SEQ_HISTORY / 2); + + } + + private void verifyProcStateHistoryDump(int count) { + final ProcStateSeqHistory history = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final IndentingPrintWriter writer = new IndentingPrintWriter( + new PrintWriter(outputStream), " "); + + if (count == 0) { + // Verify with no uid info written to history. + history.dumpUL(writer); + writer.flush(); + assertEquals("When no uid info is there, dump should contain NONE", + "NONE", outputStream.toString().trim()); + return; + } + + int uid = 111; + long procStateSeq = 222; + // Add count items and verify dump works correctly. + for (int i = 0; i < count; ++i) { + uid++; + procStateSeq++; + history.addProcStateSeqUL(uid, procStateSeq); + } + history.dumpUL(writer); + writer.flush(); + final String[] uidsDump = outputStream.toString().split(LINE_SEPARATOR); + // Dump will have at most MAX_PROC_STATE_SEQ_HISTORY items. + final int expectedCount = (count < MAX_PROC_STATE_SEQ_HISTORY) + ? count : MAX_PROC_STATE_SEQ_HISTORY; + assertEquals(expectedCount, uidsDump.length); + for (int i = 0; i < expectedCount; ++i) { + assertEquals(ProcStateSeqHistory.getString(uid, procStateSeq), uidsDump[i]); + uid--; + procStateSeq--; + } + } + private static long parseTime(String time) { final Time result = new Time(); result.parse3339(time); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java new file mode 100644 index 000000000000..6a050add5191 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 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.am; + +import static org.junit.Assert.assertEquals; + +import android.app.ActivityManagerInternal; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.AppOpsService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test class for {@link ActivityManagerInternal}. + * + * To run the tests, use + * + * runtest -c com.android.server.am.ActivityManagerInternalTest frameworks-services + * + * or the following steps: + * + * Build: m FrameworksServicesTests + * Install: adb install -r \ + * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk + * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerInternalTest -w \ + * com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ActivityManagerInternalTest { + private ActivityManagerService mAms; + private ActivityManagerInternal mAmi; + @Before + public void setUp() { + mAms = new ActivityManagerService((AppOpsService) null); + mAmi = mAms.new LocalService(); + } + + @Test + public void testNotifyNetworkPolicyRulesUpdated() { + // For checking there is no crash when there are no active uid records. + mAmi.notifyNetworkPolicyRulesUpdated(111, 11); + + // Insert active uid records. + final UidRecord record1 = addActiveUidRecord(222, 22); + final UidRecord record2 = addActiveUidRecord(333, 33); + // Notify that network policy rules are updated for uid 222. + mAmi.notifyNetworkPolicyRulesUpdated(222, 44); + assertEquals("UidRecord for uid 222 should be updated", + 44L, record1.lastNetworkUpdatedProcStateSeq); + assertEquals("UidRecord for uid 333 should not be updated", + 33L, record2.lastNetworkUpdatedProcStateSeq); + } + + private UidRecord addActiveUidRecord(int uid, long lastNetworkUpdatedProcStateSeq) { + final UidRecord record = new UidRecord(uid); + record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq; + mAms.mActiveUids.put(uid, record); + return record; + } +} |