diff options
| author | 2017-07-03 10:02:45 +0000 | |
|---|---|---|
| committer | 2017-07-03 10:02:45 +0000 | |
| commit | c523f4dc0c9226738a53b7d85279f4d151273b6f (patch) | |
| tree | 25c57f9dbf80f6bbfd5db91002c085fea5774efc | |
| parent | 31361be8125221f01ca9aa5e4ae525df7fc28935 (diff) | |
| parent | 78526c33410584011f502ca5bb379663565fbb14 (diff) | |
Merge "Add dumpsys support to RulesManagerService" am: 348a1d635d am: 06c2f8e346 am: 61ea5e6c8f
am: 78526c3341
Change-Id: I2e4e2d5291d59933f2c6225384b2fb7464fb0e57
9 files changed, 314 insertions, 7 deletions
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java index 5503ce1cf973..1eb9f45f48f1 100644 --- a/core/java/android/app/timezone/DistroRulesVersion.java +++ b/core/java/android/app/timezone/DistroRulesVersion.java @@ -125,4 +125,8 @@ public final class DistroRulesVersion implements Parcelable { + ", mRevision='" + mRevision + '\'' + '}'; } + + public String toDumpString() { + return mRulesVersion + "," + mRevision; + } } diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java index 05e97c7ef1e9..fe82dc4f1572 100644 --- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java +++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java @@ -32,6 +32,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.text.ParseException; +import java.io.PrintWriter; import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_FAILURE; import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_SUCCESS; @@ -375,4 +376,8 @@ final class PackageStatusStorage { } return value; } + + public void dump(PrintWriter printWriter) { + printWriter.println("Package status: " + getPackageStatus()); + } } diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index f9af2eae92cd..e8dfd779a715 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -26,6 +26,7 @@ import android.provider.TimeZoneRulesDataContract; import android.util.Slog; import java.io.File; +import java.io.PrintWriter; /** * Monitors the installed applications associated with time zone updates. If the app packages are @@ -510,4 +511,23 @@ public class PackageTracker implements IntentHelper.Listener { Slog.wtf(TAG, message, cause); throw new RuntimeException(message, cause); } + + public void dump(PrintWriter fout) { + fout.println("PackageTrackerState: " + toString()); + mPackageStatusStorage.dump(fout); + } + + @Override + public String toString() { + return "PackageTracker{" + + "mTrackingEnabled=" + mTrackingEnabled + + ", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' + + ", mDataAppPackageName='" + mDataAppPackageName + '\'' + + ", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis + + ", mFailedCheckRetryCount=" + mFailedCheckRetryCount + + ", mLastTriggerTimestamp=" + mLastTriggerTimestamp + + ", mCheckTriggered=" + mCheckTriggered + + ", mCheckFailureCount=" + mCheckFailureCount + + '}'; + } } diff --git a/services/core/java/com/android/server/timezone/PermissionHelper.java b/services/core/java/com/android/server/timezone/PermissionHelper.java index ba91c7f7b7ab..2ec31e2f5dfc 100644 --- a/services/core/java/com/android/server/timezone/PermissionHelper.java +++ b/services/core/java/com/android/server/timezone/PermissionHelper.java @@ -16,10 +16,14 @@ package com.android.server.timezone; +import java.io.PrintWriter; + /** * An easy-to-mock interface around permission checks for use by {@link RulesManagerService}. */ public interface PermissionHelper { void enforceCallerHasPermission(String requiredPermission) throws SecurityException; + + boolean checkDumpPermission(String tag, PrintWriter printWriter); } diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index 5724398210e4..3d60dcf6ac8e 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -37,12 +37,24 @@ import android.os.RemoteException; import android.util.Slog; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import libcore.icu.ICU; +import libcore.util.ZoneInfoDB; + +import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED; +import static android.app.timezone.RulesState.DISTRO_STATUS_NONE; +import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN; +import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL; +import static android.app.timezone.RulesState.STAGED_OPERATION_NONE; +import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL; +import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN; // TODO(nfuller) Add EventLog calls where useful in the system server. // TODO(nfuller) Check logging best practices in the system server. @@ -113,6 +125,11 @@ public final class RulesManagerService extends IRulesManager.Stub { public RulesState getRulesState() { mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); + return getRulesStateInternal(); + } + + /** Like {@link #getRulesState()} without the permission check. */ + private RulesState getRulesStateInternal() { synchronized(this) { String systemRulesVersion; try { @@ -126,18 +143,18 @@ public final class RulesManagerService extends IRulesManager.Stub { // Determine the staged operation status, if possible. DistroRulesVersion stagedDistroRulesVersion = null; - int stagedOperationStatus = RulesState.STAGED_OPERATION_UNKNOWN; + int stagedOperationStatus = STAGED_OPERATION_UNKNOWN; if (!operationInProgress) { StagedDistroOperation stagedDistroOperation; try { stagedDistroOperation = mInstaller.getStagedDistroOperation(); if (stagedDistroOperation == null) { - stagedOperationStatus = RulesState.STAGED_OPERATION_NONE; + stagedOperationStatus = STAGED_OPERATION_NONE; } else if (stagedDistroOperation.isUninstall) { - stagedOperationStatus = RulesState.STAGED_OPERATION_UNINSTALL; + stagedOperationStatus = STAGED_OPERATION_UNINSTALL; } else { // Must be an install. - stagedOperationStatus = RulesState.STAGED_OPERATION_INSTALL; + stagedOperationStatus = STAGED_OPERATION_INSTALL; DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion; stagedDistroRulesVersion = new DistroRulesVersion( stagedDistroVersion.rulesVersion, @@ -150,16 +167,16 @@ public final class RulesManagerService extends IRulesManager.Stub { // Determine the installed distro state, if possible. DistroVersion installedDistroVersion; - int distroStatus = RulesState.DISTRO_STATUS_UNKNOWN; + int distroStatus = DISTRO_STATUS_UNKNOWN; DistroRulesVersion installedDistroRulesVersion = null; if (!operationInProgress) { try { installedDistroVersion = mInstaller.getInstalledDistroVersion(); if (installedDistroVersion == null) { - distroStatus = RulesState.DISTRO_STATUS_NONE; + distroStatus = DISTRO_STATUS_NONE; installedDistroRulesVersion = null; } else { - distroStatus = RulesState.DISTRO_STATUS_INSTALLED; + distroStatus = DISTRO_STATUS_INSTALLED; installedDistroRulesVersion = new DistroRulesVersion( installedDistroVersion.rulesVersion, installedDistroVersion.revision); @@ -358,6 +375,87 @@ public final class RulesManagerService extends IRulesManager.Stub { mPackageTracker.recordCheckResult(checkToken, success); } + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!mPermissionHelper.checkDumpPermission(TAG, pw)) { + return; + } + + RulesState rulesState = getRulesStateInternal(); + if (args != null && args.length == 2) { + // Formatting options used for automated tests. The format is less free-form than + // the -format options, which are intended to be easier to parse. + if ("-format_state".equals(args[0]) && args[1] != null) { + for (char c : args[1].toCharArray()) { + switch (c) { + case 'p': // Report operation in progress + pw.println("Operation in progress: " + + rulesState.isOperationInProgress()); + break; + case 's': // Report system image rules version + pw.println("System rules version: " + + rulesState.getSystemRulesVersion()); + break; + case 'c': // Report current installation state + pw.println("Current install state: " + + distroStatusToString(rulesState.getDistroStatus())); + break; + case 'i': // Report currently installed version + DistroRulesVersion installedRulesVersion = + rulesState.getInstalledDistroRulesVersion(); + pw.print("Installed rules version: "); + if (installedRulesVersion == null) { + pw.println("<None>"); + } else { + pw.println(installedRulesVersion.toDumpString()); + } + break; + case 'o': // Report staged operation type + int stagedOperationType = rulesState.getStagedOperationType(); + pw.println("Staged operation: " + + stagedOperationToString(stagedOperationType)); + break; + case 't': + // Report staged version (i.e. the one that will be installed next boot + // if the staged operation is an install). + pw.print("Staged rules version: "); + DistroRulesVersion stagedDistroRulesVersion = + rulesState.getStagedDistroRulesVersion(); + if (stagedDistroRulesVersion == null) { + pw.println("<None>"); + } else { + pw.println("Staged install version: " + + stagedDistroRulesVersion.toDumpString()); + } + break; + case 'a': + // Report the active rules version (i.e. the rules in use by the current + // process). + pw.println("Active rules version (ICU, libcore): " + + ICU.getTZDataVersion() + "," + + ZoneInfoDB.getInstance().getVersion()); + break; + default: + pw.println("Unknown option: " + c); + } + } + return; + } + } + + pw.println("RulesManagerService state: " + toString()); + pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + "," + + ZoneInfoDB.getInstance().getVersion()); + mPackageTracker.dump(pw); + } + + @Override + public String toString() { + return "RulesManagerService{" + + "mOperationInProgress=" + mOperationInProgress + + '}'; + } + private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) { CheckToken checkToken; try { @@ -368,4 +466,30 @@ public final class RulesManagerService extends IRulesManager.Stub { } return checkToken; } + + private static String distroStatusToString(int distroStatus) { + switch(distroStatus) { + case DISTRO_STATUS_NONE: + return "None"; + case DISTRO_STATUS_INSTALLED: + return "Installed"; + case DISTRO_STATUS_UNKNOWN: + default: + return "Unknown"; + } + } + + private static String stagedOperationToString(int stagedOperationType) { + switch(stagedOperationType) { + case STAGED_OPERATION_NONE: + return "None"; + case STAGED_OPERATION_UNINSTALL: + return "Uninstall"; + case STAGED_OPERATION_INSTALL: + return "Install"; + case STAGED_OPERATION_UNKNOWN: + default: + return "Unknown"; + } + } } diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java index 482d8e2c8014..767f0e0993ef 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java +++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java @@ -17,10 +17,13 @@ package com.android.server.timezone; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.ParcelFileDescriptor; import java.io.FileInputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.concurrent.Executor; import libcore.io.Streams; @@ -40,6 +43,19 @@ final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor mContext.enforceCallingPermission(requiredPermission, null /* message */); } + @Override + public boolean checkDumpPermission(String tag, PrintWriter pw) { + // TODO(nfuller): Switch to DumpUtils.checkDumpPermission() when it is available in AOSP. + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump LocationManagerService from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return false; + } + return true; + } + // TODO Wake lock required? @Override public void execute(Runnable runnable) { diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java index dd56072372af..b57cac06ce6e 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java @@ -25,6 +25,8 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; @@ -228,4 +230,27 @@ public class PackageStatusStorageTest { assertFalse(writeOk2); assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); } + + @Test + public void dump() { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + // Dump initial state. + mPackageStatusStorage.dump(printWriter); + + // No crash and it does something. + assertFalse(stringWriter.toString().isEmpty()); + + // Reset + stringWriter.getBuffer().setLength(0); + assertTrue(stringWriter.toString().isEmpty()); + + // Store something. + mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); + + mPackageStatusStorage.dump(printWriter); + + assertFalse(stringWriter.toString().isEmpty()); + } } diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java index 4c7680b1edef..a972e4f84204 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java @@ -30,6 +30,9 @@ import android.provider.TimeZoneRulesDataContract; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; +import java.io.PrintWriter; +import java.io.StringWriter; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -1174,6 +1177,16 @@ public class PackageTrackerTest { assertFalse(token1.equals(token2)); } + @Test + public void dump() { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + mPackageTracker.dump(printWriter); + + assertFalse(stringWriter.toString().isEmpty()); + } + private void simulatePackageInstallation(PackageVersions packageVersions) throws Exception { configureApplicationsValidManifests(packageVersions); diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java index 1407e2649d59..2887e3bb520f 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java @@ -32,11 +32,15 @@ import android.app.timezone.RulesState; import android.os.ParcelFileDescriptor; import java.io.File; +import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.concurrent.Executor; import javax.annotation.Nullable; +import libcore.io.IoUtils; + import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -52,6 +56,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; /** @@ -724,6 +729,97 @@ public class RulesManagerServiceTest { verifyPackageTrackerCalled(null /* token */, true /* success */); } + @Test + public void dump_noPermission() throws Exception { + when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class))) + .thenReturn(false); + + doDumpCallAndCapture(mRulesManagerService, null); + verifyZeroInteractions(mMockPackageTracker, mMockTimeZoneDistroInstaller); + } + + @Test + public void dump_emptyArgs() throws Exception { + doSuccessfulDumpCall(mRulesManagerService, new String[0]); + + // Verify the package tracker was consulted. + verify(mMockPackageTracker).dump(any(PrintWriter.class)); + } + + @Test + public void dump_nullArgs() throws Exception { + doSuccessfulDumpCall(mRulesManagerService, null); + // Verify the package tracker was consulted. + verify(mMockPackageTracker).dump(any(PrintWriter.class)); + } + + @Test + public void dump_unknownArgs() throws Exception { + String dumpedTextUnknownArgs = doSuccessfulDumpCall( + mRulesManagerService, new String[] { "foo", "bar"}); + + // Verify the package tracker was consulted. + verify(mMockPackageTracker).dump(any(PrintWriter.class)); + + String dumpedTextZeroArgs = doSuccessfulDumpCall(mRulesManagerService, null); + assertEquals(dumpedTextZeroArgs, dumpedTextUnknownArgs); + } + + @Test + public void dump_formatState() throws Exception { + // Just expect these to not throw exceptions, not return nothing, and not interact with the + // package tracker. + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("p")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("s")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("c")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("i")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("o")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("t")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("a")); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("z" /* Unknown */)); + doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("piscotz")); + + verifyZeroInteractions(mMockPackageTracker); + } + + private static String[] dumpFormatArgs(String argsString) { + return new String[] { "-format_state", argsString}; + } + + private String doSuccessfulDumpCall(RulesManagerService rulesManagerService, String[] args) + throws Exception { + when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class))) + .thenReturn(true); + + // Set up the mocks to return (arbitrary) information about the current device state. + when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a"); + when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn( + new DistroVersion(2, 3, "2017b", 4)); + when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn( + StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7))); + + // Do the dump call. + String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args); + + assertFalse(dumpedOutput.isEmpty()); + + return dumpedOutput; + } + + private static String doDumpCallAndCapture( + RulesManagerService rulesManagerService, String[] args) throws IOException { + File file = File.createTempFile("dump", null); + try { + try (FileOutputStream fos = new FileOutputStream(file)) { + FileDescriptor fd = fos.getFD(); + rulesManagerService.dump(fd, args); + } + return IoUtils.readFileAsString(file.getAbsolutePath()); + } finally { + file.delete(); + } + } + private void verifyNoPackageTrackerCallsMade() { verifyNoMoreInteractions(mMockPackageTracker); reset(mMockPackageTracker); |