diff options
233 files changed, 6948 insertions, 2250 deletions
diff --git a/Android.bp b/Android.bp index 4ef3af069103..70b1fa0e793f 100644 --- a/Android.bp +++ b/Android.bp @@ -135,6 +135,8 @@ java_library { "core/java/android/content/pm/IPackageStatsObserver.aidl", "core/java/android/content/pm/IPinItemRequest.aidl", "core/java/android/content/pm/IShortcutService.aidl", + "core/java/android/content/pm/dex/IArtManager.aidl", + "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl", "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl", "core/java/android/database/IContentObserver.aidl", ":libcamera_client_aidl", diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 57a61ec8218f..92ee7ccfc294 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -22,6 +22,11 @@ import android.perftests.utils.PerfStatusReporter; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; +import android.content.res.ColorStateList; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.style.TextAppearanceSpan; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,40 +38,35 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) public class StaticLayoutPerfTest { - public StaticLayoutPerfTest() { - } + public StaticLayoutPerfTest() {} @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - private static final String FIXED_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing " - + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad " - + "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " - + "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse " - + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " - + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - private static final int FIXED_TEXT_LENGTH = FIXED_TEXT.length(); + private static final int WORD_LENGTH = 9; // Random word has 9 characters. + private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line. + private static final int PARA_LENGTH = 500; // Number of characters in a paragraph. - private static TextPaint PAINT = new TextPaint(); - private static final int TEXT_WIDTH = 20 * (int) PAINT.getTextSize(); + private static final boolean NO_STYLE_TEXT = false; + private static final boolean STYLE_TEXT = true; - @Test - public void testCreate() { - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { - StaticLayout.Builder.obtain(FIXED_TEXT, 0, FIXED_TEXT_LENGTH, PAINT, TEXT_WIDTH) - .build(); - } - } + private final Random mRandom = new Random(31415926535L); private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final int ALPHABET_LENGTH = ALPHABET.length(); - private static final int PARA_LENGTH = 500; + private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000); + private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" }; + private static final int[] STYLES = { + Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC + }; + private final char[] mBuffer = new char[PARA_LENGTH]; - private final Random mRandom = new Random(31415926535L); - private CharSequence generateRandomParagraph(int wordLen) { + private static TextPaint PAINT = new TextPaint(); + private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); + + private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) { for (int i = 0; i < PARA_LENGTH; i++) { if (i % (wordLen + 1) == wordLen) { mBuffer[i] = ' '; @@ -74,29 +74,112 @@ public class StaticLayoutPerfTest { mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH)); } } - return CharBuffer.wrap(mBuffer); + + CharSequence cs = CharBuffer.wrap(mBuffer); + if (!applyRandomStyle) { + return cs; + } + + SpannableStringBuilder ssb = new SpannableStringBuilder(cs); + for (int i = 0; i < ssb.length(); i += WORD_LENGTH) { + final int spanStart = i; + final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH; + + final TextAppearanceSpan span = new TextAppearanceSpan( + FAMILIES[mRandom.nextInt(FAMILIES.length)], + STYLES[mRandom.nextInt(STYLES.length)], + 24 + mRandom.nextInt(32), // text size. min 24 max 56 + TEXT_COLOR, TEXT_COLOR); + + ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + return ssb; + } + + @Test + public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + while (state.keepRunning()) { + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .build(); + } + } + + @Test + public void testCreate_RandomText_NoStyled_Greedy_NoHyphenation() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .build(); + } } - // This tries to simulate the case where the cache hit rate is low, and most of the text is - // new text. @Test - public void testCreateRandom() { + public void testCreate_RandomText_NoStyled_Greedy_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { - final CharSequence text = generateRandomParagraph(9); + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .build(); } } @Test - public void testCreateRandom_breakBalanced() { + public void testCreate_RandomText_NoStyled_Balanced_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { - final CharSequence text = generateRandomParagraph(9); + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .build(); } } + + @Test + public void testCreate_RandomText_NoStyled_Balanced_Hyphenation() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .build(); + } + } + + @Test + public void testCreate_RandomText_Styled_Greedy_NoHyphenation() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + state.resumeTiming(); + + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .build(); + } + } } diff --git a/api/current.txt b/api/current.txt index d230aea89eab..f16ff1406e82 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1470,6 +1470,8 @@ package android { field public static final int vendor = 16843751; // 0x10103e7 field public static final int version = 16844057; // 0x1010519 field public static final int versionCode = 16843291; // 0x101021b + field public static final int versionCodeMajor = 16844150; // 0x1010576 + field public static final int versionMajor = 16844151; // 0x1010577 field public static final int versionName = 16843292; // 0x101021c field public static final int verticalCorrection = 16843322; // 0x101023a field public static final int verticalDivider = 16843054; // 0x101012e @@ -6388,6 +6390,7 @@ package android.app.admin { method public deprecated boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); + method public boolean isLogoutButtonEnabled(); method public boolean isManagedProfile(android.content.ComponentName); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isNetworkLoggingEnabled(android.content.ComponentName); @@ -6430,6 +6433,7 @@ package android.app.admin { method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); method public void setLockTaskFeatures(android.content.ComponentName, int); method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException; + method public void setLogoutButtonEnabled(android.content.ComponentName, boolean); method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence); method public void setMasterVolumeMuted(android.content.ComponentName, boolean); method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int); @@ -6767,6 +6771,7 @@ package android.app.backup { method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException; method public void onQuotaExceeded(long, long); method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException; + method public void onRestore(android.app.backup.BackupDataInput, long, android.os.ParcelFileDescriptor) throws java.io.IOException; method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException; method public void onRestoreFinished(); field public static final int TYPE_DIRECTORY = 2; // 0x2 @@ -10590,6 +10595,8 @@ package android.content.pm { public class PackageInfo implements android.os.Parcelable { ctor public PackageInfo(); method public int describeContents(); + method public long getLongVersionCode(); + method public void setLongVersionCode(long); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInfo> CREATOR; field public static final int INSTALL_LOCATION_AUTO = 0; // 0x0 @@ -10619,7 +10626,7 @@ package android.content.pm { field public android.content.pm.Signature[] signatures; field public java.lang.String[] splitNames; field public int[] splitRevisionCodes; - field public int versionCode; + field public deprecated int versionCode; field public java.lang.String versionName; } @@ -11134,9 +11141,10 @@ package android.content.pm { method public int describeContents(); method public android.content.pm.VersionedPackage getDeclaringPackage(); method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); + method public long getLongVersion(); method public java.lang.String getName(); method public int getType(); - method public int getVersion(); + method public deprecated int getVersion(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR; field public static final int TYPE_BUILTIN = 0; // 0x0 @@ -11230,9 +11238,11 @@ package android.content.pm { public final class VersionedPackage implements android.os.Parcelable { ctor public VersionedPackage(java.lang.String, int); + ctor public VersionedPackage(java.lang.String, long); method public int describeContents(); + method public long getLongVersionCode(); method public java.lang.String getPackageName(); - method public int getVersionCode(); + method public deprecated int getVersionCode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR; } @@ -15494,6 +15504,8 @@ package android.hardware.camera2 { field public static final int CONTROL_AF_MODE_EDOF = 5; // 0x5 field public static final int CONTROL_AF_MODE_MACRO = 2; // 0x2 field public static final int CONTROL_AF_MODE_OFF = 0; // 0x0 + field public static final int CONTROL_AF_SCENE_CHANGE_DETECTED = 1; // 0x1 + field public static final int CONTROL_AF_SCENE_CHANGE_NOT_DETECTED = 0; // 0x0 field public static final int CONTROL_AF_STATE_ACTIVE_SCAN = 3; // 0x3 field public static final int CONTROL_AF_STATE_FOCUSED_LOCKED = 4; // 0x4 field public static final int CONTROL_AF_STATE_INACTIVE = 0; // 0x0 @@ -15768,6 +15780,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureResult.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_MODE; field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS; + field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_SCENE_CHANGE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_STATE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_TRIGGER; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_AWB_LOCK; @@ -25975,6 +25988,23 @@ package android.net { enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED; } + public final class MacAddress implements android.os.Parcelable { + method public int addressType(); + method public int describeContents(); + method public static android.net.MacAddress fromBytes(byte[]); + method public static android.net.MacAddress fromString(java.lang.String); + method public boolean isLocallyAssigned(); + method public byte[] toByteArray(); + method public java.lang.String toSafeString(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.net.MacAddress BROADCAST_ADDRESS; + field public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; + field public static final int TYPE_BROADCAST = 3; // 0x3 + field public static final int TYPE_MULTICAST = 2; // 0x2 + field public static final int TYPE_UNICAST = 1; // 0x1 + field public static final int TYPE_UNKNOWN = 0; // 0x0 + } + public class MailTo { method public java.lang.String getBody(); method public java.lang.String getCc(); @@ -39468,6 +39498,7 @@ package android.telecom { method public final int getState(); method public final android.telecom.StatusHints getStatusHints(); method public final android.telecom.Connection.VideoProvider getVideoProvider(); + method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream); method public final boolean isRingbackRequested(); method public void onAbort(); method public void onAnswer(int); @@ -39484,8 +39515,10 @@ package android.telecom { method public void onReject(java.lang.String); method public void onSeparate(); method public void onShowIncomingCallUi(); + method public void onStartRtt(android.telecom.Connection.RttTextStream); method public void onStateChanged(int); method public void onStopDtmfTone(); + method public void onStopRtt(); method public void onUnhold(); method public static java.lang.String propertiesToString(int); method public final void putExtras(android.os.Bundle); @@ -39493,6 +39526,10 @@ package android.telecom { method public final void removeExtras(java.lang.String...); method public void requestBluetoothAudio(java.lang.String); method public void sendConnectionEvent(java.lang.String, android.os.Bundle); + method public final void sendRemoteRttRequest(); + method public final void sendRttInitiationFailure(int); + method public final void sendRttInitiationSuccess(); + method public final void sendRttSessionRemotelyTerminated(); method public final void setActive(); method public final void setAddress(android.net.Uri, int); method public final void setAudioModeIsVoip(boolean); @@ -39546,6 +39583,7 @@ package android.telecom { field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 + field public static final int PROPERTY_IS_RTT = 256; // 0x100 field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_DIALING = 3; // 0x3 @@ -39565,6 +39603,12 @@ package android.telecom { field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4 } + public static final class Connection.RttTextStream { + method public java.lang.String read() throws java.io.IOException; + method public java.lang.String readImmediately() throws java.io.IOException; + method public void write(java.lang.String) throws java.io.IOException; + } + public static abstract class Connection.VideoProvider { ctor public Connection.VideoProvider(); method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities); @@ -39605,7 +39649,9 @@ package android.telecom { method public android.telecom.PhoneAccountHandle getAccountHandle(); method public android.net.Uri getAddress(); method public android.os.Bundle getExtras(); + method public android.telecom.Connection.RttTextStream getRttTextStream(); method public int getVideoState(); + method public boolean isRequestingRtt(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR; } @@ -49081,6 +49127,7 @@ package android.view.textclassifier { method public android.content.Intent getSecondaryIntent(int); method public java.lang.CharSequence getSecondaryLabel(int); method public android.view.View.OnClickListener getSecondaryOnClickListener(int); + method public java.lang.String getSignature(); method public java.lang.String getText(); } @@ -49095,6 +49142,7 @@ package android.view.textclassifier { method public android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String); method public android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); + method public android.view.textclassifier.TextClassification.Builder setSignature(java.lang.String); method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String); } @@ -49159,12 +49207,14 @@ package android.view.textclassifier { method public int getEntityCount(); method public int getSelectionEndIndex(); method public int getSelectionStartIndex(); + method public java.lang.String getSignature(); } public static final class TextSelection.Builder { ctor public TextSelection.Builder(int, int); method public android.view.textclassifier.TextSelection build(); method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float); + method public android.view.textclassifier.TextSelection.Builder setSignature(java.lang.String); } public static final class TextSelection.Options { diff --git a/api/system-current.txt b/api/system-current.txt index 16404e47ebec..b964d8a02b45 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -126,6 +126,7 @@ package android { field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES"; field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS"; field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; + field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES"; field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES"; field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL"; field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL"; @@ -183,6 +184,7 @@ package android { } public static final class R.attr { + field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566 field public static final int searchKeyphrase = 16843871; // 0x101045f @@ -409,8 +411,9 @@ package android.app.backup { field public static final java.lang.String EXTRA_LOG_CANCEL_ALL = "android.app.backup.extra.LOG_CANCEL_ALL"; field public static final java.lang.String EXTRA_LOG_EVENT_CATEGORY = "android.app.backup.extra.LOG_EVENT_CATEGORY"; field public static final java.lang.String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID"; + field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION"; field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME"; - field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION"; + field public static final deprecated java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION"; field public static final java.lang.String EXTRA_LOG_EXCEPTION_FULL_BACKUP = "android.app.backup.extra.LOG_EXCEPTION_FULL_BACKUP"; field public static final java.lang.String EXTRA_LOG_ILLEGAL_KEY = "android.app.backup.extra.LOG_ILLEGAL_KEY"; field public static final java.lang.String EXTRA_LOG_MANIFEST_PACKAGE_NAME = "android.app.backup.extra.LOG_MANIFEST_PACKAGE_NAME"; @@ -780,13 +783,15 @@ package android.content.pm { public final class InstantAppResolveInfo implements android.os.Parcelable { ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int); + ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long); ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>); method public int describeContents(); method public byte[] getDigestBytes(); method public int getDigestPrefix(); method public java.util.List<android.content.pm.InstantAppIntentFilter> getIntentFilters(); + method public long getLongVersionCode(); method public java.lang.String getPackageName(); - method public int getVersionCode(); + method public deprecated int getVersionCode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR; } @@ -843,6 +848,7 @@ package android.content.pm { public abstract class PackageManager { method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String); + method public android.content.pm.dex.ArtManager getArtManager(); method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int); method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String); @@ -950,6 +956,24 @@ package android.content.pm { } +package android.content.pm.dex { + + public class ArtManager { + method public boolean isRuntimeProfilingEnabled(); + method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler); + field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1 + field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2 + field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0 + } + + public static abstract class ArtManager.SnapshotRuntimeProfileCallback { + ctor public ArtManager.SnapshotRuntimeProfileCallback(); + method public abstract void onError(int); + method public abstract void onSuccess(android.os.ParcelFileDescriptor); + } + +} + package android.content.pm.permission { public final class RuntimePermissionPresentationInfo implements android.os.Parcelable { @@ -4106,6 +4130,7 @@ package android.telephony { method public java.lang.String getCdmaMdn(int); method public java.lang.String getCdmaMin(); method public java.lang.String getCdmaMin(int); + method public java.lang.String getCdmaPrlVersion(); method public int getCurrentPhoneType(); method public int getCurrentPhoneType(int); method public deprecated boolean getDataEnabled(); diff --git a/api/test-current.txt b/api/test-current.txt index ef898a46a2a9..7e0731a6548e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -600,32 +600,6 @@ package android.service.quicksettings { } -package android.telecom { - - public abstract class Connection extends android.telecom.Conferenceable { - method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream); - method public void onStartRtt(android.telecom.Connection.RttTextStream); - method public void onStopRtt(); - method public final void sendRemoteRttRequest(); - method public final void sendRttInitiationFailure(int); - method public final void sendRttInitiationSuccess(); - method public final void sendRttSessionRemotelyTerminated(); - field public static final int PROPERTY_IS_RTT = 256; // 0x100 - } - - public static final class Connection.RttTextStream { - method public java.lang.String read() throws java.io.IOException; - method public java.lang.String readImmediately() throws java.io.IOException; - method public void write(java.lang.String) throws java.io.IOException; - } - - public final class ConnectionRequest implements android.os.Parcelable { - method public android.telecom.Connection.RttTextStream getRttTextStream(); - method public boolean isRequestingRtt(); - } - -} - package android.telephony { public class MbmsDownloadSession implements java.lang.AutoCloseable { diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index bc63f5905e23..c29164781e47 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -86,7 +86,9 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(msg); - flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second); + // TODO: THIS CHECK FAILS BECAUSE ONCE UIDMAP SIZE EXCEEDS LIMIT, DROPPING METRICS DATA + // DOESN'T HELP. FIX THIS. + // flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second); } // Hard-coded logic to update the isolated uid's in the uid-map. diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 0e9cd3b2a148..fa7fe0c0bae7 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -551,7 +551,7 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) { return NO_ERROR; } -Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, +Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version, const vector<String16>& app) { VLOG("StatsService::informAllUidData was called"); @@ -566,7 +566,7 @@ Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<i return Status::ok(); } -Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) { +Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) { VLOG("StatsService::informOnePackage was called"); if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 03bc6d93ee03..bdae1ef62ff0 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -54,9 +54,9 @@ public: virtual Status statsCompanionReady(); virtual Status informAnomalyAlarmFired(); virtual Status informPollAlarmFired(); - virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, + virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version, const vector<String16>& app); - virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version); + virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version); virtual Status informOnePackageRemoved(const String16& app, int32_t uid); virtual Status writeDataToDisk(); diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index dd84cf4b4b93..02aca1aa58a2 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -39,11 +39,11 @@ CombinationConditionTracker::~CombinationConditionTracker() { VLOG("~CombinationConditionTracker() %s", mName.c_str()); } -bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig, +bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<string, int>& conditionNameIndexMap, vector<bool>& stack) { - VLOG("Combiniation condition init() %s", mName.c_str()); + VLOG("Combination predicate init() %s", mName.c_str()); if (mInitialized) { return true; } @@ -51,22 +51,22 @@ bool CombinationConditionTracker::init(const vector<Condition>& allConditionConf // mark this node as visited in the recursion stack. stack[mIndex] = true; - Condition_Combination combinationCondition = allConditionConfig[mIndex].combination(); + Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination(); if (!combinationCondition.has_operation()) { return false; } mLogicalOperation = combinationCondition.operation(); - if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) { + if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) { return false; } - for (string child : combinationCondition.condition()) { + for (string child : combinationCondition.predicate()) { auto it = conditionNameIndexMap.find(child); if (it == conditionNameIndexMap.end()) { - ALOGW("Condition %s not found in the config", child.c_str()); + ALOGW("Predicate %s not found in the config", child.c_str()); return false; } @@ -154,7 +154,7 @@ void CombinationConditionTracker::evaluateCondition( } } nonSlicedConditionCache[mIndex] = ConditionState::kUnknown; - ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(), + ALOGD("CombinationPredicate %s sliced may changed? %d", mName.c_str(), conditionChangedCache[mIndex] == true); } } diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 00dc6b7445b4..93369144ecd6 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -30,7 +30,7 @@ public: ~CombinationConditionTracker(); - bool init(const std::vector<Condition>& allConditionConfig, + bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<std::string, int>& conditionNameIndexMap, std::vector<bool>& stack) override; @@ -47,7 +47,7 @@ public: private: LogicalOperation mLogicalOperation; - // Store index of the children Conditions. + // Store index of the children Predicates. // We don't store string name of the Children, because we want to get rid of the hash map to // map the name to object. We don't want to store smart pointers to children, because it // increases the risk of circular dependency and memory leak. diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index b85d8c1e8eca..6f66ad685ccf 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -45,12 +45,12 @@ public: // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also // be done in the constructor, but we do it separately because (1) easy to return a bool to // indicate whether the initialization is successful. (2) makes unit test easier. - // allConditionConfig: the list of all Condition config from statsd_config. + // allConditionConfig: the list of all Predicate config from statsd_config. // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also // need to call init() on children conditions) // conditionNameIndexMap: the mapping from condition name to its index. // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. - virtual bool init(const std::vector<Condition>& allConditionConfig, + virtual bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<std::string, int>& conditionNameIndexMap, std::vector<bool>& stack) = 0; @@ -118,4 +118,3 @@ protected: } // namespace statsd } // namespace os } // namespace android - diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 50cd130e9f39..18b93ee0fd47 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -34,16 +34,16 @@ using std::vector; SimpleConditionTracker::SimpleConditionTracker( const ConfigKey& key, const string& name, const int index, - const SimpleCondition& simpleCondition, + const SimplePredicate& simplePredicate, const unordered_map<string, int>& trackerNameIndexMap) : ConditionTracker(name, index), mConfigKey(key) { VLOG("creating SimpleConditionTracker %s", mName.c_str()); - mCountNesting = simpleCondition.count_nesting(); + mCountNesting = simplePredicate.count_nesting(); - if (simpleCondition.has_start()) { - auto pair = trackerNameIndexMap.find(simpleCondition.start()); + if (simplePredicate.has_start()) { + auto pair = trackerNameIndexMap.find(simplePredicate.start()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str()); + ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str()); return; } mStartLogMatcherIndex = pair->second; @@ -52,10 +52,10 @@ SimpleConditionTracker::SimpleConditionTracker( mStartLogMatcherIndex = -1; } - if (simpleCondition.has_stop()) { - auto pair = trackerNameIndexMap.find(simpleCondition.stop()); + if (simplePredicate.has_stop()) { + auto pair = trackerNameIndexMap.find(simplePredicate.stop()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str()); + ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str()); return; } mStopLogMatcherIndex = pair->second; @@ -64,10 +64,10 @@ SimpleConditionTracker::SimpleConditionTracker( mStopLogMatcherIndex = -1; } - if (simpleCondition.has_stop_all()) { - auto pair = trackerNameIndexMap.find(simpleCondition.stop_all()); + if (simplePredicate.has_stop_all()) { + auto pair = trackerNameIndexMap.find(simplePredicate.stop_all()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop all matcher %s not found in the config", simpleCondition.stop().c_str()); + ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str()); return; } mStopAllLogMatcherIndex = pair->second; @@ -76,14 +76,14 @@ SimpleConditionTracker::SimpleConditionTracker( mStopAllLogMatcherIndex = -1; } - mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(), - simpleCondition.dimension().end()); + mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(), + simplePredicate.dimension().end()); if (mOutputDimension.size() > 0) { mSliced = true; } - if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) { + if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { mInitialValue = ConditionState::kFalse; } else { mInitialValue = ConditionState::kUnknown; @@ -98,7 +98,7 @@ SimpleConditionTracker::~SimpleConditionTracker() { VLOG("~SimpleConditionTracker()"); } -bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig, +bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<string, int>& conditionNameIndexMap, vector<bool>& stack) { @@ -139,7 +139,7 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); + ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); return true; } } @@ -221,7 +221,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou conditionChangedCache[mIndex] = changed; conditionCache[mIndex] = newCondition; - VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(), + VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(), conditionChangedCache[mIndex] == true); } @@ -292,13 +292,13 @@ void SimpleConditionTracker::isConditionMet( (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second; if (pair == conditionParameters.end() && mOutputDimension.size() > 0) { - ALOGE("Condition %s output has dimension, but it's not specified in the query!", + ALOGE("Predicate %s output has dimension, but it's not specified in the query!", mName.c_str()); conditionCache[mIndex] = mInitialValue; return; } - VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str()); + VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str()); auto startedCountIt = mSlicedConditionState.find(key); if (startedCountIt == mSlicedConditionState.end()) { @@ -308,7 +308,7 @@ void SimpleConditionTracker::isConditionMet( startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; } - VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]); + VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]); } } // namespace statsd diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index d21afd1d8083..644d84c76591 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -30,12 +30,12 @@ namespace statsd { class SimpleConditionTracker : public virtual ConditionTracker { public: SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index, - const SimpleCondition& simpleCondition, + const SimplePredicate& simplePredicate, const std::unordered_map<std::string, int>& trackerNameIndexMap); ~SimpleConditionTracker(); - bool init(const std::vector<Condition>& allConditionConfig, + bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<std::string, int>& conditionNameIndexMap, std::vector<bool>& stack) override; diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 4b82e687e35b..6a76c5573b90 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -422,57 +422,57 @@ StatsdConfig build_fake_config() { simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID); - // Conditions............. - Condition* condition = config.add_condition(); - condition->set_name("SCREEN_IS_ON"); - SimpleCondition* simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("SCREEN_TURNED_ON"); - simpleCondition->set_stop("SCREEN_TURNED_OFF"); - simpleCondition->set_count_nesting(false); - - condition = config.add_condition(); - condition->set_name("SCREEN_IS_OFF"); - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("SCREEN_TURNED_OFF"); - simpleCondition->set_stop("SCREEN_TURNED_ON"); - simpleCondition->set_count_nesting(false); - - condition = config.add_condition(); - condition->set_name("APP_IS_BACKGROUND"); - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("APP_GOES_BACKGROUND"); - simpleCondition->set_stop("APP_GOES_FOREGROUND"); - KeyMatcher* condition_dimension1 = simpleCondition->add_dimension(); - condition_dimension1->set_key(APP_USAGE_UID_KEY_ID); - simpleCondition->set_count_nesting(false); - - condition = config.add_condition(); - condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON"); - Condition_Combination* combination_condition = condition->mutable_combination(); - combination_condition->set_operation(LogicalOperation::AND); - combination_condition->add_condition("APP_IS_BACKGROUND"); - combination_condition->add_condition("SCREEN_IS_ON"); - - condition = config.add_condition(); - condition->set_name("WL_HELD_PER_APP_PER_NAME"); - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("APP_GET_WL"); - simpleCondition->set_stop("APP_RELEASE_WL"); - KeyMatcher* condition_dimension = simpleCondition->add_dimension(); - condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID); - condition_dimension = simpleCondition->add_dimension(); - condition_dimension->set_key(WAKE_LOCK_NAME_KEY); - simpleCondition->set_count_nesting(true); - - condition = config.add_condition(); - condition->set_name("WL_HELD_PER_APP"); - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("APP_GET_WL"); - simpleCondition->set_stop("APP_RELEASE_WL"); - simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE); - condition_dimension = simpleCondition->add_dimension(); - condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID); - simpleCondition->set_count_nesting(true); + // Predicates............. + Predicate* predicate = config.add_predicate(); + predicate->set_name("SCREEN_IS_ON"); + SimplePredicate* simplePredicate = predicate->mutable_simple_predicate(); + simplePredicate->set_start("SCREEN_TURNED_ON"); + simplePredicate->set_stop("SCREEN_TURNED_OFF"); + simplePredicate->set_count_nesting(false); + + predicate = config.add_predicate(); + predicate->set_name("SCREEN_IS_OFF"); + simplePredicate = predicate->mutable_simple_predicate(); + simplePredicate->set_start("SCREEN_TURNED_OFF"); + simplePredicate->set_stop("SCREEN_TURNED_ON"); + simplePredicate->set_count_nesting(false); + + predicate = config.add_predicate(); + predicate->set_name("APP_IS_BACKGROUND"); + simplePredicate = predicate->mutable_simple_predicate(); + simplePredicate->set_start("APP_GOES_BACKGROUND"); + simplePredicate->set_stop("APP_GOES_FOREGROUND"); + KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension(); + predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID); + simplePredicate->set_count_nesting(false); + + predicate = config.add_predicate(); + predicate->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON"); + Predicate_Combination* combination_predicate = predicate->mutable_combination(); + combination_predicate->set_operation(LogicalOperation::AND); + combination_predicate->add_predicate("APP_IS_BACKGROUND"); + combination_predicate->add_predicate("SCREEN_IS_ON"); + + predicate = config.add_predicate(); + predicate->set_name("WL_HELD_PER_APP_PER_NAME"); + simplePredicate = predicate->mutable_simple_predicate(); + simplePredicate->set_start("APP_GET_WL"); + simplePredicate->set_stop("APP_RELEASE_WL"); + KeyMatcher* predicate_dimension = simplePredicate->add_dimension(); + predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID); + predicate_dimension = simplePredicate->add_dimension(); + predicate_dimension->set_key(WAKE_LOCK_NAME_KEY); + simplePredicate->set_count_nesting(true); + + predicate = config.add_predicate(); + predicate->set_name("WL_HELD_PER_APP"); + simplePredicate = predicate->mutable_simple_predicate(); + simplePredicate->set_start("APP_GET_WL"); + simplePredicate->set_stop("APP_RELEASE_WL"); + simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); + predicate_dimension = simplePredicate->add_dimension(); + predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID); + simplePredicate->set_count_nesting(true); return config; } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index ce38f5832047..800a2b9d186a 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -51,7 +51,8 @@ public: void finish() override; // TODO: Implement this later. - virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) + override{}; // TODO: Implement this later. virtual void notifyAppRemoved(const string& apk, const int uid) override{}; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 83fe84a8fd5b..fd484c25dbaa 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -122,16 +122,16 @@ void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime } unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const { + const HashableDimensionKey& eventKey) const { switch (mMetric.aggregation_type()) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); } } @@ -179,13 +179,6 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReportLocked continue; } - // If there is no duration bucket info for this key, don't include it in the report. - // For example, duration started, but condition is never turned to true. - // TODO: Only add the key to the map when we add duration buckets info for it. - if (pair.second.size() == 0) { - continue; - } - long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); @@ -228,7 +221,7 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReportLocked (long long)mCurrentBucketStartTimeNs); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(); startNewProtoOutputStreamLocked(endTime); - // TODO: Properly clear the old buckets. + mPastBuckets.clear(); return buffer; } @@ -238,7 +231,7 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { } VLOG("flushing..........."); for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) { - if (it->second->flushIfNeeded(eventTime)) { + if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) { VLOG("erase bucket for key %s", it->first.c_str()); it = mCurrentSlicedDuration.erase(it); } else { @@ -290,7 +283,7 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( if (hitGuardRailLocked(eventKey)) { return; } - mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]); + mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey); } auto it = mCurrentSlicedDuration.find(eventKey); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 2653df8b0214..4bf9d1c957cb 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -50,7 +50,8 @@ public: void finish() override; // TODO: Implement this later. - virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) + override{}; // TODO: Implement this later. virtual void notifyAppRemoved(const string& apk, const int uid) override{}; @@ -106,7 +107,7 @@ private: // Helper function to create a duration tracker given the metric aggregation type. std::unique_ptr<DurationTracker> createDurationTracker( - const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const; + const HashableDimensionKey& eventKey) const; // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index afb48c4b9cef..da3b3cab49ba 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -43,7 +43,8 @@ public: void finish() override; // TODO: Implement this later. - virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) + override{}; // TODO: Implement this later. virtual void notifyAppRemoved(const string& apk, const int uid) override{}; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index c839c09f953c..36705b1f3cbe 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -59,7 +59,8 @@ public: void finish() override; // TODO: Implement this later. - virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) + override{}; // TODO: Implement this later. virtual void notifyAppRemoved(const string& apk, const int uid) override{}; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 60b725d2ab55..a2efd3fa976b 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -49,7 +49,8 @@ public: void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override; // TODO: Implement this later. - virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) + override{}; // TODO: Implement this later. virtual void notifyAppRemoved(const string& apk, const int uid) override{}; diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 834f7f5b0b86..3c714b3923e8 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -63,8 +63,7 @@ public: DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : mConfigKey(key), mName(name), mEventKey(eventKey), @@ -73,7 +72,6 @@ public: mBucketSizeNs(bucketSizeNs), mNested(nesting), mCurrentBucketStartTimeNs(currentBucketStartNs), - mBucket(bucket), mDuration(0), mCurrentBucketNum(0), mAnomalyTrackers(anomalyTrackers){}; @@ -91,7 +89,9 @@ public: // Flush stale buckets if needed, and return true if the tracker has no on-going duration // events, so that the owner can safely remove the tracker. - virtual bool flushIfNeeded(uint64_t timestampNs) = 0; + virtual bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0; // Predict the anomaly timestamp given the current status. virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, @@ -159,8 +159,6 @@ protected: uint64_t mCurrentBucketStartTimeNs; - std::vector<DurationBucket>& mBucket; // where to write output - int64_t mDuration; // current recorded duration result uint64_t mCurrentBucketNum; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 4b346dd25050..08c91351c49e 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -28,10 +28,9 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers, bucket) { + bucketSizeNs, anomalyTrackers) { } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -145,7 +144,8 @@ void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { } } -bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { +bool MaxDurationTracker::flushIfNeeded( + uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return false; } @@ -202,7 +202,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { if (mDuration != 0) { info.mDuration = mDuration; - mBucket.push_back(info); + (*output)[mEventKey].push_back(info); addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" final duration for last bucket: %lld", (long long)mDuration); } @@ -215,7 +215,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { info.mBucketEndNs = endTime + mBucketSizeNs * i; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; - mBucket.push_back(info); + (*output)[mEventKey].push_back(info); addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs); } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index e0d1466c6fc4..10eddb8bee6e 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -32,15 +32,16 @@ public: const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket); + const std::vector<sp<AnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, const bool stopAll) override; void noteStopAll(const uint64_t eventTime) override; - bool flushIfNeeded(uint64_t timestampNs) override; + bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index abdfbc06d7a7..8122744b32b5 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -29,10 +29,9 @@ OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& n sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers, bucket), + bucketSizeNs, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; @@ -128,7 +127,8 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { mConditionKeyMap.clear(); } -bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { +bool OringDurationTracker::flushIfNeeded( + uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) { return false; } @@ -145,7 +145,7 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { } if (mDuration > 0) { current_info.mDuration = mDuration; - mBucket.push_back(current_info); + (*output)[mEventKey].push_back(current_info); addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum); VLOG(" duration: %lld", (long long)current_info.mDuration); } @@ -157,9 +157,9 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; - mBucket.push_back(info); - addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); - VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); + (*output)[mEventKey].push_back(info); + addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); + VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); } } mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index a8404a961dd2..b7d3cba7055e 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -31,8 +31,7 @@ public: const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket); + const std::vector<sp<AnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; @@ -43,7 +42,9 @@ public: void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; - bool flushIfNeeded(uint64_t timestampNs) override; + bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, const uint64_t currentTimestamp) const override; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 00048f5aa95d..1fc4d425a6bd 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -70,14 +70,14 @@ bool handleMetricWithConditions( unordered_map<int, std::vector<int>>& conditionToMetricMap) { auto condition_it = conditionTrackerMap.find(condition); if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find Condition \"%s\" in the config", condition.c_str()); + ALOGW("cannot find Predicate \"%s\" in the config", condition.c_str()); return false; } for (const auto& link : links) { auto it = conditionTrackerMap.find(link.condition()); if (it == conditionTrackerMap.end()) { - ALOGW("cannot find Condition \"%s\" in the config", link.condition().c_str()); + ALOGW("cannot find Predicate \"%s\" in the config", link.condition().c_str()); return false; } allConditionTrackers[condition_it->second]->setSliced(true); @@ -142,31 +142,31 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, unordered_map<string, int>& conditionTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, unordered_map<int, std::vector<int>>& trackerToConditionMap) { - vector<Condition> conditionConfigs; - const int conditionTrackerCount = config.condition_size(); + vector<Predicate> conditionConfigs; + const int conditionTrackerCount = config.predicate_size(); conditionConfigs.reserve(conditionTrackerCount); allConditionTrackers.reserve(conditionTrackerCount); for (int i = 0; i < conditionTrackerCount; i++) { - const Condition& condition = config.condition(i); + const Predicate& condition = config.predicate(i); int index = allConditionTrackers.size(); switch (condition.contents_case()) { - case Condition::ContentsCase::kSimpleCondition: { + case Predicate::ContentsCase::kSimplePredicate: { allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.name(), index, condition.simple_condition(), logTrackerMap)); + key, condition.name(), index, condition.simple_predicate(), logTrackerMap)); break; } - case Condition::ContentsCase::kCombination: { + case Predicate::ContentsCase::kCombination: { allConditionTrackers.push_back( new CombinationConditionTracker(condition.name(), index)); break; } default: - ALOGE("Condition \"%s\" malformed", condition.name().c_str()); + ALOGE("Predicate \"%s\" malformed", condition.name().c_str()); return false; } if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) { - ALOGE("Duplicate Condition found!"); + ALOGE("Duplicate Predicate found!"); return false; } conditionTrackerMap[condition.name()] = index; @@ -254,43 +254,43 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, return false; } - const Condition& durationWhat = config.condition(what_it->second); + const Predicate& durationWhat = config.predicate(what_it->second); - if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) { + if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); return false; } - const auto& simpleCondition = durationWhat.simple_condition(); + const auto& simplePredicate = durationWhat.simple_predicate(); - bool nesting = simpleCondition.count_nesting(); + bool nesting = simplePredicate.count_nesting(); int trackerIndices[3] = {-1, -1, -1}; - if (!simpleCondition.has_start() || - !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex, + if (!simplePredicate.has_start() || + !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex, metric.dimension_size() > 0, allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[0])) { ALOGE("Duration metrics must specify a valid the start event matcher"); return false; } - if (simpleCondition.has_stop() && - !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex, + if (simplePredicate.has_stop() && + !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex, metric.dimension_size() > 0, allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[1])) { return false; } - if (simpleCondition.has_stop_all() && - !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex, + if (simplePredicate.has_stop_all() && + !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex, metric.dimension_size() > 0, allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[2])) { return false; } vector<KeyMatcher> internalDimension; - internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(), - simpleCondition.dimension().end()); + internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(), + simplePredicate.dimension().end()); int conditionIndex = -1; diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h index 5aa3db573c70..bc8b0deb7231 100644 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ b/cmds/statsd/src/packages/PackageInfoListener.h @@ -28,7 +28,7 @@ class PackageInfoListener : public virtual android::RefBase { public: // Uid map will notify this listener that the app with apk name and uid has been upgraded to // the specified version. - virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0; + virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int64_t version) = 0; // Notify interested listeners that the given apk and uid combination no longer exits. virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0; diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index db592e2c5841..6e7a613b05a3 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -23,6 +23,8 @@ #include <binder/IServiceManager.h> #include <utils/Errors.h> +#include <inttypes.h> + using namespace android; namespace android { @@ -46,7 +48,7 @@ bool UidMap::hasApp(int uid, const string& packageName) const { return false; } -int UidMap::getAppVersion(int uid, const string& packageName) const { +int64_t UidMap::getAppVersion(int uid, const string& packageName) const { lock_guard<mutex> lock(mMutex); auto range = mMap.equal_range(uid); @@ -58,13 +60,13 @@ int UidMap::getAppVersion(int uid, const string& packageName) const { return 0; } -void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode, +void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode, const vector<String16>& packageName) { updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName); } void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, - const vector<int32_t>& versionCode, const vector<String16>& packageName) { + const vector<int64_t>& versionCode, const vector<String16>& packageName) { lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. mMap.clear(); @@ -87,12 +89,12 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, ensureBytesUsedBelowLimit(); } -void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) { +void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) { updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode); } void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, - const int32_t& versionCode) { + const int64_t& versionCode) { lock_guard<mutex> lock(mMutex); string app = string(String8(app_16).string()); @@ -116,7 +118,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i auto range = mMap.equal_range(int(uid)); for (auto it = range.first; it != range.second; ++it) { if (it->second.packageName == app) { - it->second.versionCode = int(versionCode); + it->second.versionCode = versionCode; return; } VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid); @@ -124,7 +126,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i } // Otherwise, we need to add an app at this uid. - mMap.insert(make_pair(uid, AppData(app, int(versionCode)))); + mMap.insert(make_pair(uid, AppData(app, versionCode))); } void UidMap::ensureBytesUsedBelowLimit() { @@ -298,8 +300,8 @@ void UidMap::printUidMap(FILE* out) { lock_guard<mutex> lock(mMutex); for (auto it : mMap) { - fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, - it.first); + fprintf(out, "%s, v%" PRId64 " (%i)\n", it.second.packageName.c_str(), + it.second.versionCode, it.first); } } diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index d2971c9b8a8d..9e1ad6946af2 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -41,9 +41,9 @@ namespace statsd { struct AppData { const string packageName; - int versionCode; + int64_t versionCode; - AppData(const string& a, const int v) : packageName(a), versionCode(v){}; + AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){}; }; // UidMap keeps track of what the corresponding app name (APK name) and version code for every uid @@ -57,16 +57,16 @@ public: * All three inputs must be the same size, and the jth element in each array refers to the same * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. */ - void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode, + void updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode, const vector<String16>& packageName); - void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode); + void updateApp(const String16& packageName, const int32_t& uid, const int64_t& versionCode); void removeApp(const String16& packageName, const int32_t& uid); // Returns true if the given uid contains the specified app (eg. com.google.android.gms). bool hasApp(int uid, const string& packageName) const; - int getAppVersion(int uid, const string& packageName) const; + int64_t getAppVersion(int uid, const string& packageName) const; // Helper for debugging contents of this uid map. Can be triggered with: // adb shell cmd stats print-uid-map @@ -104,10 +104,10 @@ public: private: void updateMap(const int64_t& timestamp, const vector<int32_t>& uid, - const vector<int32_t>& versionCode, const vector<String16>& packageName); + const vector<int64_t>& versionCode, const vector<String16>& packageName); void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid, - const int32_t& versionCode); + const int64_t& versionCode); void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid); UidMapping getOutput(const int64_t& timestamp, const ConfigKey& key); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index cc8a26de1a8d..60d9a3dbb9d2 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -102,7 +102,7 @@ message UidMapping { message PackageInfo { optional string name = 1; - optional int32 version = 2; + optional int64 version = 2; optional int32 uid = 3; } @@ -119,7 +119,7 @@ message UidMapping { optional string app = 3; optional int32 uid = 4; - optional int32 version = 5; + optional int64 version = 5; } repeated Change changes = 2; } diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 6837be098b19..ca63b033a625 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -75,7 +75,7 @@ message AtomMatcher { } } -message SimpleCondition { +message SimplePredicate { optional string start = 1; optional string stop = 2; @@ -93,17 +93,17 @@ message SimpleCondition { repeated KeyMatcher dimension = 6; } -message Condition { +message Predicate { optional string name = 1; message Combination { optional LogicalOperation operation = 1; - repeated string condition = 2; + repeated string predicate = 2; } oneof contents { - SimpleCondition simple_condition = 2; + SimplePredicate simple_predicate = 2; Combination combination = 3; } } @@ -232,7 +232,7 @@ message StatsdConfig { repeated AtomMatcher atom_matcher = 7; - repeated Condition condition = 8; + repeated Predicate predicate = 8; repeated Alert alert = 9; } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index e4750e9ccf36..c6a531089db7 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -34,7 +34,7 @@ using android::sp; using std::set; using std::unordered_map; using std::vector; -using android::os::statsd::Condition; +using android::os::statsd::Predicate; #ifdef __ANDROID__ @@ -165,7 +165,7 @@ StatsdConfig buildMissingMatchers() { return config; } -StatsdConfig buildMissingCondition() { +StatsdConfig buildMissingPredicate() { StatsdConfig config; config.set_name("12345"); @@ -223,7 +223,7 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { return config; } -StatsdConfig buildCircleConditions() { +StatsdConfig buildCirclePredicates() { StatsdConfig config; config.set_name("12345"); @@ -247,19 +247,19 @@ StatsdConfig buildCircleConditions() { simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - auto condition = config.add_condition(); + auto condition = config.add_predicate(); condition->set_name("SCREEN_IS_ON"); - SimpleCondition* simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("SCREEN_IS_ON"); - simpleCondition->set_stop("SCREEN_IS_OFF"); + SimplePredicate* simplePredicate = condition->mutable_simple_predicate(); + simplePredicate->set_start("SCREEN_IS_ON"); + simplePredicate->set_stop("SCREEN_IS_OFF"); - condition = config.add_condition(); + condition = config.add_predicate(); condition->set_name("SCREEN_IS_EITHER_ON_OFF"); - Condition_Combination* combination = condition->mutable_combination(); + Predicate_Combination* combination = condition->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_condition("SCREEN_IS_ON"); - combination->add_condition("SCREEN_IS_EITHER_ON_OFF"); + combination->add_predicate("SCREEN_IS_ON"); + combination->add_predicate("SCREEN_IS_EITHER_ON_OFF"); return config; } @@ -329,8 +329,8 @@ TEST(MetricsManagerTest, TestMissingMatchers) { conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } -TEST(MetricsManagerTest, TestMissingCondition) { - StatsdConfig config = buildMissingCondition(); +TEST(MetricsManagerTest, TestMissingPredicate) { + StatsdConfig config = buildMissingPredicate(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; vector<sp<ConditionTracker>> allConditionTrackers; @@ -344,8 +344,8 @@ TEST(MetricsManagerTest, TestMissingCondition) { conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } -TEST(MetricsManagerTest, TestCircleConditionDependency) { - StatsdConfig config = buildCircleConditions(); +TEST(MetricsManagerTest, TestCirclePredicateDependency) { + StatsdConfig config = buildCirclePredicates(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; vector<sp<ConditionTracker>> allConditionTrackers; diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index aa194e625060..5b2ceddc6858 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -61,7 +61,7 @@ TEST(UidMapTest, TestIsolatedUID) { TEST(UidMapTest, TestMatching) { UidMap m; vector<int32_t> uids; - vector<int32_t> versions; + vector<int64_t> versions; vector<String16> apps; uids.push_back(1000); @@ -79,7 +79,7 @@ TEST(UidMapTest, TestMatching) { TEST(UidMapTest, TestAddAndRemove) { UidMap m; vector<int32_t> uids; - vector<int32_t> versions; + vector<int64_t> versions; vector<String16> apps; uids.push_back(1000); @@ -107,7 +107,7 @@ TEST(UidMapTest, TestClearingOutput) { m.OnConfigUpdated(config1); vector<int32_t> uids; - vector<int32_t> versions; + vector<int64_t> versions; vector<String16> apps; uids.push_back(1000); uids.push_back(1000); @@ -161,7 +161,7 @@ TEST(UidMapTest, TestMemoryComputed) { size_t startBytes = m.mBytesUsed; vector<int32_t> uids; - vector<int32_t> versions; + vector<int64_t> versions; vector<String16> apps; uids.push_back(1000); apps.push_back(String16(kApp1.c_str())); @@ -191,7 +191,7 @@ TEST(UidMapTest, TestMemoryGuardrail) { size_t startBytes = m.mBytesUsed; vector<int32_t> uids; - vector<int32_t> versions; + vector<int64_t> versions; vector<String16> apps; for (int i = 0; i < 100; i++) { uids.push_back(1); diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 8b0b6a473768..01ba82d31549 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -30,21 +30,21 @@ namespace statsd { const ConfigKey kConfigKey(0, "test"); -SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse, +SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, bool outputSlicedUid) { - SimpleCondition simpleCondition; - simpleCondition.set_start("WAKE_LOCK_ACQUIRE"); - simpleCondition.set_stop("WAKE_LOCK_RELEASE"); - simpleCondition.set_stop_all("RELEASE_ALL"); + SimplePredicate simplePredicate; + simplePredicate.set_start("WAKE_LOCK_ACQUIRE"); + simplePredicate.set_stop("WAKE_LOCK_RELEASE"); + simplePredicate.set_stop_all("RELEASE_ALL"); if (outputSlicedUid) { - KeyMatcher* keyMatcher = simpleCondition.add_dimension(); + KeyMatcher* keyMatcher = simplePredicate.add_dimension(); keyMatcher->set_key(1); } - simpleCondition.set_count_nesting(countNesting); - simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE - : SimpleCondition_InitialValue_UNKNOWN); - return simpleCondition; + simplePredicate.set_count_nesting(countNesting); + simplePredicate.set_initial_value(defaultFalse ? SimplePredicate_InitialValue_FALSE + : SimplePredicate_InitialValue_UNKNOWN); + return simplePredicate; } void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) { @@ -68,18 +68,18 @@ map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid, } TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { - SimpleCondition simpleCondition; - simpleCondition.set_start("SCREEN_TURNED_ON"); - simpleCondition.set_stop("SCREEN_TURNED_OFF"); - simpleCondition.set_count_nesting(false); - simpleCondition.set_initial_value(SimpleCondition_InitialValue_UNKNOWN); + SimplePredicate simplePredicate; + simplePredicate.set_start("SCREEN_TURNED_ON"); + simplePredicate.set_stop("SCREEN_TURNED_OFF"); + simplePredicate.set_count_nesting(false); + simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); unordered_map<string, int> trackerNameIndexMap; trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/, - simpleCondition, trackerNameIndexMap); + simplePredicate, trackerNameIndexMap); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -87,11 +87,11 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allConditions; + vector<sp<ConditionTracker>> allPredicates; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // not matched start or stop. condition doesn't change EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); @@ -104,7 +104,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // now condition should change to true. EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -117,7 +117,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); EXPECT_FALSE(changedCache[0]); @@ -129,7 +129,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // condition changes to false. @@ -143,7 +143,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // condition should still be false. not changed. EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); @@ -151,17 +151,17 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { } TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { - SimpleCondition simpleCondition; - simpleCondition.set_start("SCREEN_TURNED_ON"); - simpleCondition.set_stop("SCREEN_TURNED_OFF"); - simpleCondition.set_count_nesting(true); + SimplePredicate simplePredicate; + simplePredicate.set_start("SCREEN_TURNED_ON"); + simplePredicate.set_stop("SCREEN_TURNED_OFF"); + simplePredicate.set_count_nesting(true); unordered_map<string, int> trackerNameIndexMap; trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", - 0 /*condition tracker index*/, simpleCondition, + 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -170,11 +170,11 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { vector<MatchingState> matcherState; matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allConditions; + vector<sp<ConditionTracker>> allPredicates; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -187,7 +187,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -200,7 +200,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // result should still be true EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -213,7 +213,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); // result should still be true EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); @@ -221,7 +221,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { } TEST(SimpleConditionTrackerTest, TestSlicedCondition) { - SimpleCondition simpleCondition = getWakeLockHeldCondition( + SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/); string conditionName = "WL_HELD_BY_UID2"; @@ -231,7 +231,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { trackerNameIndexMap["RELEASE_ALL"] = 2; SimpleConditionTracker conditionTracker(kConfigKey, conditionName, - 0 /*condition tracker index*/, simpleCondition, + 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); int uid = 111; @@ -243,11 +243,11 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allConditions; + vector<sp<ConditionTracker>> allPredicates; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); @@ -257,7 +257,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { const auto queryKey = getWakeLockQueryKey(1, uid, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid @@ -268,7 +268,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { matcherState.push_back(MatchingState::kNotMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache); EXPECT_FALSE(changedCache[0]); @@ -280,7 +280,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { matcherState.push_back(MatchingState::kMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache); // nothing changes, because wake lock 2 is still held for this uid EXPECT_FALSE(changedCache[0]); @@ -292,19 +292,19 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { matcherState.push_back(MatchingState::kMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); EXPECT_TRUE(changedCache[0]); // query again conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { - SimpleCondition simpleCondition = getWakeLockHeldCondition( + SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, false /*slice output by uid*/); string conditionName = "WL_HELD"; @@ -314,7 +314,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { trackerNameIndexMap["RELEASE_ALL"] = 2; SimpleConditionTracker conditionTracker(kConfigKey, conditionName, - 0 /*condition tracker index*/, simpleCondition, + 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); int uid1 = 111; string uid1_wl1 = "wl1_1"; @@ -329,11 +329,11 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allConditions; + vector<sp<ConditionTracker>> allPredicates; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); @@ -343,7 +343,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { map<string, HashableDimensionKey> queryKey; conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid @@ -354,7 +354,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { matcherState.push_back(MatchingState::kNotMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache); EXPECT_FALSE(changedCache[0]); @@ -366,7 +366,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { matcherState.push_back(MatchingState::kMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache); // nothing changes, because uid2 is still holding wl. EXPECT_FALSE(changedCache[0]); @@ -378,19 +378,19 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { matcherState.push_back(MatchingState::kMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); EXPECT_TRUE(changedCache[0]); // query again conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } TEST(SimpleConditionTrackerTest, TestStopAll) { - SimpleCondition simpleCondition = getWakeLockHeldCondition( + SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/); string conditionName = "WL_HELD_BY_UID3"; @@ -400,7 +400,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { trackerNameIndexMap["RELEASE_ALL"] = 2; SimpleConditionTracker conditionTracker(kConfigKey, conditionName, - 0 /*condition tracker index*/, simpleCondition, + 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); int uid1 = 111; int uid2 = 222; @@ -413,11 +413,11 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allConditions; + vector<sp<ConditionTracker>> allPredicates; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); @@ -427,7 +427,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by uid2 @@ -439,7 +439,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { matcherState.push_back(MatchingState::kNotMatched); conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache); EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); EXPECT_TRUE(changedCache[0]); @@ -448,7 +448,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -461,7 +461,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { conditionCache[0] = ConditionState::kNotEvaluated; changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache, + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache); EXPECT_TRUE(changedCache[0]); EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); @@ -470,14 +470,14 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); // TEST QUERY const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allConditions, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 3f2b7cdd22ee..3158c2757263 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -89,7 +89,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); DurationMetricProducer durationProducer( - kConfigKey, metric, 0 /*no condition*/, 1 /* start index */, 2 /* stop index */, + kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs); EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -97,12 +97,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */); durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != - durationProducer.mPastBuckets.end()); - const auto& buckets1 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; - EXPECT_EQ(0UL, buckets1.size()); - + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); durationProducer.onMatchedLogEvent(1 /* start index*/, event3, false /* scheduledPull */); durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 5d47437c486c..1adcc1146c42 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -37,18 +37,19 @@ namespace os { namespace statsd { const ConfigKey kConfigKey(0, "test"); +const string eventKey = "event"; TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("1", true, bucketStartTimeNs, key1); // Event starts again. This would not change anything as it already starts. @@ -60,50 +61,53 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { tracker.noteStart("2", true, bucketStartTimeNs + 20, key1); tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(20ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(20ULL, buckets[eventKey][0].mDuration); } TEST(MaxDurationTrackerTest, TestStopAll) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("1", true, bucketStartTimeNs + 1, key1); // Another event starts in this bucket. tracker.noteStart("2", true, bucketStartTimeNs + 20, key1); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); EXPECT_TRUE(tracker.mInfos.empty()); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(40ULL, buckets[1].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + + tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); // The event starts. tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -112,25 +116,26 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1); // The event stops at early 4th bucket. - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20); + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/); - EXPECT_EQ(3u, buckets.size()); - EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(3u, buckets[eventKey].size()); + EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration); + EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration); + EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {}); // 2 starts tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -138,20 +143,21 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { // one stop tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/); - tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1); + tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); // real stop now. tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1); + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); - EXPECT_EQ(3u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); - EXPECT_EQ(5ULL, buckets[2].mDuration); + EXPECT_EQ(3u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); + EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration); } TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { @@ -163,15 +169,15 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); @@ -180,9 +186,10 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(5ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration); } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { @@ -193,7 +200,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -202,8 +209,8 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}); tracker.noteStart("1", true, eventStartTimeNs, key1); tracker.noteStop("1", eventStartTimeNs + 10, false); @@ -211,7 +218,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(10LL, tracker.mDuration); tracker.noteStart("2", true, eventStartTimeNs + 20, key1); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets); tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false); EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration); EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 6913c8161345..fa7b9a734524 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -35,6 +35,7 @@ namespace os { namespace statsd { const ConfigKey kConfigKey(0, "test"); +const string eventKey = "event"; TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -42,15 +43,15 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -58,9 +59,11 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(durationTimeNs, buckets[0].mDuration); + tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationNested) { @@ -69,14 +72,14 @@ TEST(OringDurationTrackerTest, TestDurationNested) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl @@ -84,9 +87,10 @@ TEST(OringDurationTrackerTest, TestDurationNested) { tracker.noteStop("2:maps", eventStartTimeNs + 2000, false); tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(2003ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestStopAll) { @@ -95,23 +99,24 @@ TEST(OringDurationTrackerTest, TestStopAll) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl tracker.noteStopAll(eventStartTimeNs + 2003); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(2003ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { @@ -120,32 +125,33 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets); tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1); EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false); tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChange) { @@ -157,15 +163,15 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); @@ -173,9 +179,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(5ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChange2) { @@ -189,15 +196,15 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { .WillOnce(Return(ConditionState::kFalse)) .WillOnce(Return(ConditionState::kTrue)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -207,9 +214,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { // 2nd duration: 1000ns tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(1005ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(1005ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { @@ -221,14 +229,14 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1); @@ -239,9 +247,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(15ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(15ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { @@ -252,7 +261,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -261,8 +270,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}); // Nothing in the past bucket. tracker.noteStart("", true, eventStartTimeNs, key1); @@ -270,7 +279,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); tracker.noteStop("", eventStartTimeNs + 3, false); - EXPECT_EQ(0u, buckets.size()); + EXPECT_EQ(0u, buckets[eventKey].size()); uint64_t event1StartTimeNs = eventStartTimeNs + 10; tracker.noteStart("1", true, event1StartTimeNs, key1); @@ -279,11 +288,13 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10; - tracker.flushIfNeeded(event1StopTimeNs); + tracker.flushIfNeeded(event1StopTimeNs, &buckets); tracker.noteStop("1", event1StopTimeNs, false); - EXPECT_EQ(1u, buckets.size()); + + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10, - buckets[0].mDuration); + buckets[eventKey][0].mDuration); const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10; const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs; @@ -312,7 +323,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -321,8 +332,8 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/, + bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); tracker.noteStart("", true, eventStartTimeNs, key1); tracker.noteStop("", eventStartTimeNs + 10, false); @@ -336,7 +347,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); EXPECT_EQ((long long)(51ULL * NS_PER_SEC), (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets); tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false); EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs)); EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25), diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java index e7bb539e8b75..8d20f9759c96 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java @@ -21,7 +21,7 @@ import android.util.Log; import android.util.StatsLog; import com.android.internal.os.StatsdConfigProto.Bucket; -import com.android.internal.os.StatsdConfigProto.Condition; +import com.android.internal.os.StatsdConfigProto.Predicate; import com.android.internal.os.StatsdConfigProto.CountMetric; import com.android.internal.os.StatsdConfigProto.DurationMetric; import com.android.internal.os.StatsdConfigProto.EventConditionLink; @@ -31,7 +31,7 @@ import com.android.internal.os.StatsdConfigProto.ValueMetric; import com.android.internal.os.StatsdConfigProto.KeyMatcher; import com.android.internal.os.StatsdConfigProto.KeyValueMatcher; import com.android.internal.os.StatsdConfigProto.AtomMatcher; -import com.android.internal.os.StatsdConfigProto.SimpleCondition; +import com.android.internal.os.StatsdConfigProto.SimplePredicate; import com.android.internal.os.StatsdConfigProto.StatsdConfig; import java.io.InputStream; @@ -113,9 +113,9 @@ public class ConfigFactory { addValueMetric(metric, i, bucketMillis, config); numMetrics++; } - // conditions - for (Condition condition : mTemplate.getConditionList()) { - addCondition(condition, i, config); + // predicates + for (Predicate predicate : mTemplate.getPredicateList()) { + addPredicate(predicate, i, config); } // matchers for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) { @@ -254,30 +254,30 @@ public class ConfigFactory { } /** - * Creates a {@link Condition} based on the template. Makes sure that all names - * are appended with the provided suffix. Then adds that condition to the config. + * Creates a {@link Predicate} based on the template. Makes sure that all names + * are appended with the provided suffix. Then adds that predicate to the config. */ - private void addCondition(Condition template, int suffix, StatsdConfig.Builder config) { - Condition.Builder condition = template.toBuilder() + private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) { + Predicate.Builder predicate = template.toBuilder() .setName(template.getName() + suffix); if (template.hasCombination()) { - Condition.Combination.Builder cb = template.getCombination().toBuilder() - .clearCondition(); - for (String child : template.getCombination().getConditionList()) { - cb.addCondition(child + suffix); + Predicate.Combination.Builder cb = template.getCombination().toBuilder() + .clearPredicate(); + for (String child : template.getCombination().getPredicateList()) { + cb.addPredicate(child + suffix); } - condition.setCombination(cb.build()); + predicate.setCombination(cb.build()); } - if (template.hasSimpleCondition()) { - SimpleCondition.Builder sc = template.getSimpleCondition().toBuilder() - .setStart(template.getSimpleCondition().getStart() + suffix) - .setStop(template.getSimpleCondition().getStop() + suffix); - if (template.getSimpleCondition().hasStopAll()) { - sc.setStopAll(template.getSimpleCondition().getStopAll() + suffix); + if (template.hasSimplePredicate()) { + SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder() + .setStart(template.getSimplePredicate().getStart() + suffix) + .setStop(template.getSimplePredicate().getStop() + suffix); + if (template.getSimplePredicate().hasStopAll()) { + sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix); } - condition.setSimpleCondition(sc.build()); + predicate.setSimplePredicate(sc.build()); } - config.addCondition(condition); + config.addPredicate(predicate); } /** diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 005b7c389b26..1dbdb59ebcce 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -55,6 +55,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; +import android.content.pm.dex.ArtManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; @@ -121,6 +122,8 @@ public class ApplicationPackageManager extends PackageManager { private UserManager mUserManager; @GuardedBy("mLock") private PackageInstaller mInstaller; + @GuardedBy("mLock") + private ArtManager mArtManager; @GuardedBy("mDelegates") private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>(); @@ -2750,4 +2753,18 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowAsRuntimeException(); } } + + @Override + public ArtManager getArtManager() { + synchronized (mLock) { + if (mArtManager == null) { + try { + mArtManager = new ArtManager(mPM.getArtManager()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return mArtManager; + } + } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 21e4227bc481..84c07ec7f45a 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -667,4 +667,10 @@ interface IActivityManager { void setShowWhenLocked(in IBinder token, boolean showWhenLocked); void setTurnScreenOn(in IBinder token, boolean turnScreenOn); + + /** + * Similar to {@link #startUserInBackground(int userId), but with a listener to report + * user unlock progress. + */ + boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener); } diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl index a07374b80408..4a85efd9e4c2 100644 --- a/core/java/android/app/IBackupAgent.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -79,7 +79,7 @@ oneway interface IBackupAgent { * passed here as a convenience to the agent. */ void doRestore(in ParcelFileDescriptor data, - int appVersionCode, in ParcelFileDescriptor newState, + long appVersionCode, in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder); /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index f0117f20dfeb..acdad1c44c90 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8475,6 +8475,38 @@ public class DevicePolicyManager { } /** + * Called by a device owner to specify whether a logout button is enabled for all secondary + * users. The system may show a logout button that stops the user and switches back to the + * primary user. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled whether logout button should be enabled or not. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) { + throwIfParentInstance("setLogoutButtonEnabled"); + try { + mService.setLogoutButtonEnabled(admin, enabled); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns whether logout button is enabled by a device owner. + * + * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise. + */ + public boolean isLogoutButtonEnabled() { + throwIfParentInstance("isLogoutButtonEnabled"); + try { + return mService.isLogoutButtonEnabled(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Callback used in {@link #clearApplicationUserData} * to indicate that the clearing of an application's user data is done. */ diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 802d42f232ba..4925f341fd94 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -366,4 +366,7 @@ interface IDevicePolicyManager { StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user); boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback); + + void setLogoutButtonEnabled(in ComponentName admin, boolean enabled); + boolean isLogoutButtonEnabled(); } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 7aa80d263976..861cb9a8d035 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -263,6 +263,17 @@ public abstract class BackupAgent extends ContextWrapper { ParcelFileDescriptor newState) throws IOException; /** + * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)} + * that handles a long app version code. Default implementation casts the version code to + * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}. + */ + public void onRestore(BackupDataInput data, long appVersionCode, + ParcelFileDescriptor newState) + throws IOException { + onRestore(data, (int) appVersionCode, newState); + } + + /** * The application is having its entire file system contents backed up. {@code data} * points to the backup destination, and the app has the opportunity to choose which * files are to be stored. To commit a file as part of the backup, call the @@ -947,7 +958,7 @@ public abstract class BackupAgent extends ContextWrapper { } @Override - public void doRestore(ParcelFileDescriptor data, int appVersionCode, + public void doRestore(ParcelFileDescriptor data, long appVersionCode, ParcelFileDescriptor newState, int token, IBackupManager callbackBinder) throws RemoteException { // Ensure that we're running with the app's normal permission level diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java index ebad16e0bc3d..ae4a98a4e06e 100644 --- a/core/java/android/app/backup/BackupManagerMonitor.java +++ b/core/java/android/app/backup/BackupManagerMonitor.java @@ -40,9 +40,14 @@ public class BackupManagerMonitor { /** string : the package name */ public static final String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME"; - /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */ + /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME + * @deprecated Use {@link #EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION} */ + @Deprecated public static final String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION"; + /** long : the full versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */ + public static final String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION = + "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION"; /** int : the id of the log message, will be a unique identifier */ public static final String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID"; /** diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index 6692e137fa0a..bfee27974f57 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -136,9 +136,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } @Override - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { - mCallback.onAppStatusChanged(pluggedDevice, config, registered); + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, registered); } @Override @@ -349,7 +348,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Device are only possible when application is registered. Only one * application can be registered at time. When no longer used, application * should be unregistered using - * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * {@link #unregisterApp()}. * The registration status should be tracked by the application by handling callback from * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. @@ -382,11 +381,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - BluetoothHidDeviceAppConfiguration config = - new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = new BluetoothHidDeviceCallbackWrapper(callback); - result = service.registerApp(config, sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -407,13 +404,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. * - * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link - * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, - * BluetoothHidDeviceAppConfiguration, - * boolean)} * @return true if the command is successfully sent; otherwise false. */ - public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + public boolean unregisterApp() { Log.v(TAG, "unregisterApp()"); boolean result = false; @@ -421,7 +414,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - result = service.unregisterApp(config); + result = service.unregisterApp(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java deleted file mode 100644 index d1efa2d6fca6..000000000000 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2016 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 android.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Random; - -/** - * Represents the app configuration for a Bluetooth HID Device application. - * - * The app needs a BluetoothHidDeviceAppConfiguration token to unregister - * the Bluetooth HID Device service. - * - * {@see BluetoothHidDevice} - * - * {@hide} - */ -public final class BluetoothHidDeviceAppConfiguration implements Parcelable { - private final long mHash; - - BluetoothHidDeviceAppConfiguration() { - Random rnd = new Random(); - mHash = rnd.nextLong(); - } - - BluetoothHidDeviceAppConfiguration(long hash) { - mHash = hash; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppConfiguration) { - BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; - return mHash == config.mHash; - } - return false; - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<BluetoothHidDeviceAppConfiguration> CREATOR = - new Parcelable.Creator<BluetoothHidDeviceAppConfiguration>() { - - @Override - public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { - long hash = in.readLong(); - return new BluetoothHidDeviceAppConfiguration(hash); - } - - @Override - public BluetoothHidDeviceAppConfiguration[] newArray(int size) { - return new BluetoothHidDeviceAppConfiguration[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeLong(mHash); - } -} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java index 5ccda0dc7dd5..dc6f9fa1abf3 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -37,21 +37,17 @@ public abstract class BluetoothHidDeviceCallback { * {@link BluetoothHidDevice#registerApp * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} * or - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * {@link BluetoothHidDevice#unregisterApp()} * , but can be also unsolicited in case e.g. Bluetooth was turned off in * which case application is unregistered automatically. * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has * Virtual Cable established with device. Only valid when application is registered, can be * <code>null</code>. - * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token - * required to unregister application using - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}. * @param registered <code>true</code> if application is registered, <code>false</code> * otherwise. */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d1e12aa02c34..55ad5c5dd1b6 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4462,11 +4462,19 @@ public class Intent implements Parcelable, Cloneable { /** * The version code of the app to install components from. + * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE). * @hide */ + @Deprecated public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE"; /** + * The version code of the app to install components from. + * @hide + */ + public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; + + /** * The app that triggered the ephemeral installation. * @hide */ diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 5298f57b4ef2..84b1ff3219a2 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -896,7 +896,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * The app's declared version code. * @hide */ - public int versionCode; + public long versionCode; /** * The user-visible SDK version (ex. 26) of the framework against which the application claims @@ -1323,7 +1323,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(uid); dest.writeInt(minSdkVersion); dest.writeInt(targetSdkVersion); - dest.writeInt(versionCode); + dest.writeLong(versionCode); dest.writeInt(enabled ? 1 : 0); dest.writeInt(enabledSetting); dest.writeInt(installLocation); @@ -1392,7 +1392,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { uid = source.readInt(); minSdkVersion = source.readInt(); targetSdkVersion = source.readInt(); - versionCode = source.readInt(); + versionCode = source.readLong(); enabled = source.readInt() != 0; enabledSetting = source.readInt(); installLocation = source.readInt(); diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java index 067363d43adf..6bdcefbe974e 100644 --- a/core/java/android/content/pm/AuxiliaryResolveInfo.java +++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java @@ -45,7 +45,7 @@ public final class AuxiliaryResolveInfo extends IntentFilter { /** Opaque token to track the instant application resolution */ public final String token; /** The version code of the package */ - public final int versionCode; + public final long versionCode; /** An intent to start upon failure to install */ public final Intent failureIntent; @@ -71,7 +71,7 @@ public final class AuxiliaryResolveInfo extends IntentFilter { public AuxiliaryResolveInfo(@NonNull String packageName, @Nullable String splitName, @Nullable ComponentName failureActivity, - int versionCode, + long versionCode, @Nullable Intent failureIntent) { super(); this.packageName = packageName; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 64d33d543e4a..56a0def27602 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -48,6 +48,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; +import android.content.pm.dex.IArtManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; @@ -657,4 +658,6 @@ interface IPackageManager { ComponentName getInstantAppInstallerComponent(); String getInstantAppAndroidId(String packageName, int userId); + + IArtManager getArtManager(); } diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 22e994f4cfa8..fb3094c70953 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -44,10 +44,15 @@ public final class InstantAppResolveInfo implements Parcelable { /** The filters used to match domain */ private final List<InstantAppIntentFilter> mFilters; /** The version code of the app that this class resolves to */ - private final int mVersionCode; + private final long mVersionCode; public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List<InstantAppIntentFilter> filters, int versionCode) { + this(digest, packageName, filters, (long)versionCode); + } + + public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, + @Nullable List<InstantAppIntentFilter> filters, long versionCode) { // validate arguments if ((packageName == null && (filters != null && filters.size() != 0)) || (packageName != null && (filters == null || filters.size() == 0))) { @@ -74,7 +79,7 @@ public final class InstantAppResolveInfo implements Parcelable { mPackageName = in.readString(); mFilters = new ArrayList<InstantAppIntentFilter>(); in.readList(mFilters, null /*loader*/); - mVersionCode = in.readInt(); + mVersionCode = in.readLong(); } public byte[] getDigestBytes() { @@ -93,7 +98,15 @@ public final class InstantAppResolveInfo implements Parcelable { return mFilters; } + /** + * @deprecated Use {@link #getLongVersionCode} instead. + */ + @Deprecated public int getVersionCode() { + return (int) (mVersionCode & 0xffffffff); + } + + public long getLongVersionCode() { return mVersionCode; } @@ -107,7 +120,7 @@ public final class InstantAppResolveInfo implements Parcelable { out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); - out.writeInt(mVersionCode); + out.writeLong(mVersionCode); } public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index f8889b6853d6..0c893b0ff06a 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -37,13 +37,56 @@ public class PackageInfo implements Parcelable { public String[] splitNames; /** + * @deprecated Use {@link #getLongVersionCode()} instead, which includes both + * this and the additional + * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} attribute. * The version number of this package, as specified by the <manifest> * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode} * attribute. + * @see #getLongVersionCode() */ + @Deprecated public int versionCode; /** + * @hide + * The major version number of this package, as specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} + * attribute. + * @see #getLongVersionCode() + */ + public int versionCodeMajor; + + /** + * Return {@link android.R.styleable#AndroidManifest_versionCode versionCode} and + * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} combined + * together as a single long value. The + * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} is placed in + * the upper 32 bits. + */ + public long getLongVersionCode() { + return composeLongVersionCode(versionCodeMajor, versionCode); + } + + /** + * Set the full version code in this PackageInfo, updating {@link #versionCode} + * with the lower bits. + * @see #getLongVersionCode() + */ + public void setLongVersionCode(long longVersionCode) { + versionCodeMajor = (int) (longVersionCode>>32); + versionCode = (int) longVersionCode; + } + + /** + * @hide Internal implementation for composing a minor and major version code in to + * a single long version code. + */ + public static long composeLongVersionCode(int major, int minor) { + return (((long) major) << 32) | (((long) minor) & 0xffffffffL); + } + + /** * The version name of this package, as specified by the <manifest> * tag's {@link android.R.styleable#AndroidManifest_versionName versionName} * attribute. @@ -333,6 +376,7 @@ public class PackageInfo implements Parcelable { dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); + dest.writeInt(versionCodeMajor); dest.writeString(versionName); dest.writeInt(baseRevisionCode); dest.writeIntArray(splitRevisionCodes); @@ -389,6 +433,7 @@ public class PackageInfo implements Parcelable { packageName = source.readString(); splitNames = source.createStringArray(); versionCode = source.readInt(); + versionCodeMajor = source.readInt(); versionName = source.readString(); baseRevisionCode = source.readInt(); splitRevisionCodes = source.createIntArray(); diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index 1efe082b7fdf..bbf020d76c92 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -38,9 +38,27 @@ public class PackageInfoLite implements Parcelable { /** * The android:versionCode of the package. + * @deprecated Use {@link #getLongVersionCode()} instead, which includes both + * this and the additional + * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute. */ + @Deprecated public int versionCode; + /** + * @hide + * The android:versionCodeMajor of the package. + */ + public int versionCodeMajor; + + /** + * Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a + * single long value. The {@link #versionCodeMajor} is placed in the upper 32 bits. + */ + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); + } + /** Revision code of base APK */ public int baseRevisionCode; /** Revision codes of any split APKs, ordered by parsed splitName */ @@ -55,10 +73,10 @@ public class PackageInfoLite implements Parcelable { /** * Specifies the recommended install location. Can be one of - * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage - * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media - * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors - * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors. + * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage, + * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media, + * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors, + * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors. */ public int recommendedInstallLocation; public int installLocation; @@ -82,6 +100,7 @@ public class PackageInfoLite implements Parcelable { dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); + dest.writeInt(versionCodeMajor); dest.writeInt(baseRevisionCode); dest.writeIntArray(splitRevisionCodes); dest.writeInt(recommendedInstallLocation); @@ -111,6 +130,7 @@ public class PackageInfoLite implements Parcelable { packageName = source.readString(); splitNames = source.createStringArray(); versionCode = source.readInt(); + versionCodeMajor = source.readInt(); baseRevisionCode = source.readInt(); splitRevisionCodes = source.createIntArray(); recommendedInstallLocation = source.readInt(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f796aabee2d1..ff02c4030941 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -42,6 +42,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.dex.ArtManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Rect; @@ -2634,13 +2635,22 @@ public abstract class PackageManager { /** * Extra field name for the version code of a package pending verification. - * + * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead. * @hide */ + @Deprecated public static final String EXTRA_VERIFICATION_VERSION_CODE = "android.content.pm.extra.VERIFICATION_VERSION_CODE"; /** + * Extra field name for the long version code of a package pending verification. + * + * @hide + */ + public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE = + "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE"; + + /** * Extra field name for the ID of a intent filter pending verification. * Passed to an intent filter verifier and is used to call back to * {@link #verifyIntentFilter} @@ -5842,4 +5852,14 @@ public abstract class PackageManager { @SystemApi public abstract void registerDexModule(String dexModulePath, @Nullable DexModuleRegisterCallback callback); + + /** + * Returns the {@link ArtManager} associated with this package manager. + * + * @hide + */ + @SystemApi + public @NonNull ArtManager getArtManager() { + throw new UnsupportedOperationException("getArtManager not implemented in subclass"); + } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 98c824dbf8f6..153c944a8d93 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -394,6 +394,7 @@ public class PackageParser { public static class PackageLite { public final String packageName; public final int versionCode; + public final int versionCodeMajor; public final int installLocation; public final VerifierInfo[] verifiers; @@ -436,6 +437,7 @@ public class PackageParser { String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; + this.versionCodeMajor = baseApk.versionCodeMajor; this.installLocation = baseApk.installLocation; this.verifiers = baseApk.verifiers; this.splitNames = splitNames; @@ -476,6 +478,7 @@ public class PackageParser { public final String configForSplit; public final String usesSplitName; public final int versionCode; + public final int versionCodeMajor; public final int revisionCode; public final int installLocation; public final VerifierInfo[] verifiers; @@ -489,11 +492,11 @@ public class PackageParser { public final boolean isolatedSplits; public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit, - String configForSplit, String usesSplitName, int versionCode, int revisionCode, - int installLocation, List<VerifierInfo> verifiers, Signature[] signatures, - Certificate[][] certificates, boolean coreApp, boolean debuggable, - boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs, - boolean isolatedSplits) { + String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor, + int revisionCode, int installLocation, List<VerifierInfo> verifiers, + Signature[] signatures, Certificate[][] certificates, boolean coreApp, + boolean debuggable, boolean multiArch, boolean use32bitAbi, + boolean extractNativeLibs, boolean isolatedSplits) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; @@ -501,6 +504,7 @@ public class PackageParser { this.configForSplit = configForSplit; this.usesSplitName = usesSplitName; this.versionCode = versionCode; + this.versionCodeMajor = versionCodeMajor; this.revisionCode = revisionCode; this.installLocation = installLocation; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); @@ -513,6 +517,10 @@ public class PackageParser { this.extractNativeLibs = extractNativeLibs; this.isolatedSplits = isolatedSplits; } + + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); + } } /** @@ -663,6 +671,7 @@ public class PackageParser { pi.packageName = p.packageName; pi.splitNames = p.splitNames; pi.versionCode = p.mVersionCode; + pi.versionCodeMajor = p.mVersionCodeMajor; pi.baseRevisionCode = p.baseRevisionCode; pi.splitRevisionCodes = p.splitRevisionCodes; pi.versionName = p.mVersionName; @@ -1880,6 +1889,7 @@ public class PackageParser { int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; + int versionCodeMajor = 0; int revisionCode = 0; boolean coreApp = false; boolean debuggable = false; @@ -1898,6 +1908,8 @@ public class PackageParser { PARSE_DEFAULT_INSTALL_LOCATION); } else if (attr.equals("versionCode")) { versionCode = attrs.getAttributeIntValue(i, 0); + } else if (attr.equals("versionCodeMajor")) { + versionCodeMajor = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("revisionCode")) { revisionCode = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("coreApp")) { @@ -1963,9 +1975,9 @@ public class PackageParser { } return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, - configForSplit, usesSplitName, versionCode, revisionCode, installLocation, - verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi, - extractNativeLibs, isolatedSplits); + configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode, + installLocation, verifiers, signatures, certificates, coreApp, debuggable, + multiArch, use32bitAbi, extractNativeLibs, isolatedSplits); } /** @@ -2086,8 +2098,11 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifest); - pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger( + pkg.mVersionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); + pkg.mVersionCodeMajor = sa.getInteger( + com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0); + pkg.applicationInfo.versionCode = pkg.getLongVersionCode(); pkg.baseRevisionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_revisionCode, 0); pkg.mVersionName = sa.getNonConfigurationString( @@ -2912,7 +2927,7 @@ public class PackageParser { 1, additionalCertSha256Digests.length); pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname); - pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt( + pkg.usesStaticLibrariesVersions = ArrayUtils.appendLong( pkg.usesStaticLibrariesVersions, version, true); pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class, pkg.usesStaticLibrariesCertDigests, certSha256Digests, true); @@ -3867,6 +3882,9 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestStaticLibrary_name); final int version = sa.getInt( com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1); + final int versionMajor = sa.getInt( + com.android.internal.R.styleable.AndroidManifestStaticLibrary_versionMajor, + 0); sa.recycle(); @@ -3894,7 +3912,12 @@ public class PackageParser { } owner.staticSharedLibName = lname.intern(); - owner.staticSharedLibVersion = version; + if (version >= 0) { + owner.staticSharedLibVersion = + PackageInfo.composeLongVersionCode(versionMajor, version); + } else { + owner.staticSharedLibVersion = version; + } ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY; XmlUtils.skipCurrentTag(parser); @@ -5895,11 +5918,11 @@ public class PackageParser { public ArrayList<Package> childPackages; public String staticSharedLibName = null; - public int staticSharedLibVersion = 0; + public long staticSharedLibVersion = 0; public ArrayList<String> libraryNames = null; public ArrayList<String> usesLibraries = null; public ArrayList<String> usesStaticLibraries = null; - public int[] usesStaticLibrariesVersions = null; + public long[] usesStaticLibrariesVersions = null; public String[][] usesStaticLibrariesCertDigests = null; public ArrayList<String> usesOptionalLibraries = null; public String[] usesLibraryFiles = null; @@ -5916,6 +5939,14 @@ public class PackageParser { // The version code declared for this package. public int mVersionCode; + // The major version code declared for this package. + public int mVersionCodeMajor; + + // Return long containing mVersionCode and mVersionCodeMajor. + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode); + } + // The version name declared for this package. public String mVersionName; @@ -6390,7 +6421,7 @@ public class PackageParser { if (staticSharedLibName != null) { staticSharedLibName = staticSharedLibName.intern(); } - staticSharedLibVersion = dest.readInt(); + staticSharedLibVersion = dest.readLong(); libraryNames = dest.createStringArrayList(); internStringArrayList(libraryNames); usesLibraries = dest.createStringArrayList(); @@ -6404,8 +6435,8 @@ public class PackageParser { usesStaticLibraries = new ArrayList<>(libCount); dest.readStringList(usesStaticLibraries); internStringArrayList(usesStaticLibraries); - usesStaticLibrariesVersions = new int[libCount]; - dest.readIntArray(usesStaticLibrariesVersions); + usesStaticLibrariesVersions = new long[libCount]; + dest.readLongArray(usesStaticLibrariesVersions); usesStaticLibrariesCertDigests = new String[libCount][]; for (int i = 0; i < libCount; i++) { usesStaticLibrariesCertDigests[i] = dest.createStringArray(); @@ -6545,7 +6576,7 @@ public class PackageParser { dest.writeParcelableList(childPackages, flags); dest.writeString(staticSharedLibName); - dest.writeInt(staticSharedLibVersion); + dest.writeLong(staticSharedLibVersion); dest.writeStringList(libraryNames); dest.writeStringList(usesLibraries); dest.writeStringList(usesOptionalLibraries); @@ -6556,7 +6587,7 @@ public class PackageParser { } else { dest.writeInt(usesStaticLibraries.size()); dest.writeStringList(usesStaticLibraries); - dest.writeIntArray(usesStaticLibrariesVersions); + dest.writeLongArray(usesStaticLibrariesVersions); for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) { dest.writeStringArray(usesStaticLibrariesCertDigest); } diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index aea843adbd48..56d61efdcb25 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -361,7 +361,7 @@ public abstract class RegisteredServicesCache<V> { } IntArray updatedUids = null; for (ServiceInfo<V> service : allServices) { - int versionCode = service.componentInfo.applicationInfo.versionCode; + long versionCode = service.componentInfo.applicationInfo.versionCode; String pkg = service.componentInfo.packageName; ApplicationInfo newAppInfo = null; try { diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 7d301a3154f0..2f1b256dccdf 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -73,8 +73,7 @@ public final class SharedLibraryInfo implements Parcelable { private final String mName; - // TODO: Make long when we change the paltform to use longs - private final int mVersion; + private final long mVersion; private final @Type int mType; private final VersionedPackage mDeclaringPackage; private final List<VersionedPackage> mDependentPackages; @@ -90,7 +89,7 @@ public final class SharedLibraryInfo implements Parcelable { * * @hide */ - public SharedLibraryInfo(String name, int version, int type, + public SharedLibraryInfo(String name, long version, int type, VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) { mName = name; mVersion = version; @@ -100,7 +99,7 @@ public final class SharedLibraryInfo implements Parcelable { } private SharedLibraryInfo(Parcel parcel) { - this(parcel.readString(), parcel.readInt(), parcel.readInt(), + this(parcel.readString(), parcel.readLong(), parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null)); } @@ -124,6 +123,14 @@ public final class SharedLibraryInfo implements Parcelable { } /** + * @deprecated Use {@link #getLongVersion()} instead. + */ + @Deprecated + public @IntRange(from = -1) int getVersion() { + return mVersion < 0 ? (int) mVersion : (int) (mVersion & 0x7fffffff); + } + + /** * Gets the version of the library. For {@link #TYPE_STATIC static} libraries * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these @@ -131,7 +138,7 @@ public final class SharedLibraryInfo implements Parcelable { * * @return The version. */ - public @IntRange(from = -1) int getVersion() { + public @IntRange(from = -1) long getLongVersion() { return mVersion; } @@ -192,7 +199,7 @@ public final class SharedLibraryInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mName); - parcel.writeInt(mVersion); + parcel.writeLong(mVersion); parcel.writeInt(mType); parcel.writeParcelable(mDeclaringPackage, flags); parcel.writeList(mDependentPackages); diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java index 29c5efe7c77a..395346641b1e 100644 --- a/core/java/android/content/pm/VersionedPackage.java +++ b/core/java/android/content/pm/VersionedPackage.java @@ -28,7 +28,7 @@ import java.lang.annotation.RetentionPolicy; */ public final class VersionedPackage implements Parcelable { private final String mPackageName; - private final int mVersionCode; + private final long mVersionCode; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -47,9 +47,21 @@ public final class VersionedPackage implements Parcelable { mVersionCode = versionCode; } + /** + * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST} + * to refer to the highest version code of this package. + * @param packageName The package name. + * @param versionCode The version code. + */ + public VersionedPackage(@NonNull String packageName, + @VersionCode long versionCode) { + mPackageName = packageName; + mVersionCode = versionCode; + } + private VersionedPackage(Parcel parcel) { mPackageName = parcel.readString(); - mVersionCode = parcel.readInt(); + mVersionCode = parcel.readLong(); } /** @@ -62,11 +74,19 @@ public final class VersionedPackage implements Parcelable { } /** + * @deprecated use {@link #getLongVersionCode()} instead. + */ + @Deprecated + public @VersionCode int getVersionCode() { + return (int) (mVersionCode & 0x7fffffff); + } + + /** * Gets the version code. * * @return The version code. */ - public @VersionCode int getVersionCode() { + public @VersionCode long getLongVersionCode() { return mVersionCode; } @@ -83,7 +103,7 @@ public final class VersionedPackage implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mPackageName); - parcel.writeInt(mVersionCode); + parcel.writeLong(mVersionCode); } public static final Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() { diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java new file mode 100644 index 000000000000..201cd8d32cc1 --- /dev/null +++ b/core/java/android/content/pm/dex/ArtManager.java @@ -0,0 +1,156 @@ +/** + * Copyright 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 android.content.pm.dex; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Class for retrieving various kinds of information related to the runtime artifacts of + * packages that are currently installed on the device. + * + * @hide + */ +@SystemApi +public class ArtManager { + private static final String TAG = "ArtManager"; + + /** The snapshot failed because the package was not found. */ + public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; + /** The snapshot failed because the package code path does not exist. */ + public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; + /** The snapshot failed because of an internal error (e.g. error during opening profiles). */ + public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; + + private IArtManager mArtManager; + + /** + * @hide + */ + public ArtManager(@NonNull IArtManager manager) { + mArtManager = manager; + } + + /** + * Snapshots the runtime profile for an apk belonging to the package {@code packageName}. + * The apk is identified by {@code codePath}. The calling process must have + * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * The result will be posted on {@code handler} using the given {@code callback}. + * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}. + * + * @param packageName the target package name + * @param codePath the code path for which the profile should be retrieved + * @param callback the callback which should be used for the result + * @param handler the handler which should be used to post the result + */ + @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) + public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath, + @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) { + Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath); + + SnapshotRuntimeProfileCallbackDelegate delegate = + new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper()); + try { + mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** + * Returns true if runtime profiles are enabled, false otherwise. + * + * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + */ + @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) + public boolean isRuntimeProfilingEnabled() { + try { + return mArtManager.isRuntimeProfilingEnabled(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Callback used for retrieving runtime profiles. + */ + public abstract static class SnapshotRuntimeProfileCallback { + /** + * Called when the profile snapshot finished with success. + * + * @param profileReadFd the file descriptor that can be used to read the profile. Note that + * the file might be empty (which is valid profile). + */ + public abstract void onSuccess(ParcelFileDescriptor profileReadFd); + + /** + * Called when the profile snapshot finished with an error. + * + * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND, + * SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}. + */ + public abstract void onError(int errCode); + } + + private static class SnapshotRuntimeProfileCallbackDelegate + extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub + implements Handler.Callback { + private static final int MSG_SNAPSHOT_OK = 1; + private static final int MSG_ERROR = 2; + private final ArtManager.SnapshotRuntimeProfileCallback mCallback; + private final Handler mHandler; + + private SnapshotRuntimeProfileCallbackDelegate( + ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) { + mCallback = callback; + mHandler = new Handler(looper, this); + } + + @Override + public void onSuccess(ParcelFileDescriptor profileReadFd) { + mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget(); + } + + @Override + public void onError(int errCode) { + mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget(); + } + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_SNAPSHOT_OK: + mCallback.onSuccess((ParcelFileDescriptor) msg.obj); + break; + case MSG_ERROR: + mCallback.onError(msg.arg1); + break; + default: return false; + } + return true; + } + } +} diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl new file mode 100644 index 000000000000..8cbb452344b2 --- /dev/null +++ b/core/java/android/content/pm/dex/IArtManager.aidl @@ -0,0 +1,44 @@ +/* +** Copyright 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 android.content.pm.dex; + +import android.content.pm.dex.ISnapshotRuntimeProfileCallback; + +/** + * A system service that provides access to runtime and compiler artifacts. + * + * @hide + */ +interface IArtManager { + /** + * Snapshots the runtime profile for an apk belonging to the package {@param packageName}. + * The apk is identified by {@param codePath}. The calling process must have + * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * The result will be posted on {@param callback} with the profile being available as a + * read-only {@link android.os.ParcelFileDescriptor}. + */ + oneway void snapshotRuntimeProfile(in String packageName, + in String codePath, in ISnapshotRuntimeProfileCallback callback); + + /** + * Returns true if runtime profiles are enabled, false otherwise. + * + * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + */ + boolean isRuntimeProfilingEnabled(); +} diff --git a/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl new file mode 100644 index 000000000000..3b4838fc7824 --- /dev/null +++ b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl @@ -0,0 +1,29 @@ +/* +** Copyright 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 android.content.pm.dex; + +import android.os.ParcelFileDescriptor; + +/** + * Callback used to post the result of a profile-snapshot operation. + * + * @hide + */ +oneway interface ISnapshotRuntimeProfileCallback { + void onSuccess(in ParcelFileDescriptor profileReadFd); + void onError(int errCode); +} diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 4b57018bfddf..a7a3df7ff372 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2725,6 +2725,22 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AWB_STATE_LOCKED = 3; // + // Enumeration values for CaptureResult#CONTROL_AF_SCENE_CHANGE + // + + /** + * <p>Scene change is not detected within AF regions.</p> + * @see CaptureResult#CONTROL_AF_SCENE_CHANGE + */ + public static final int CONTROL_AF_SCENE_CHANGE_NOT_DETECTED = 0; + + /** + * <p>Scene change is detected within AF regions.</p> + * @see CaptureResult#CONTROL_AF_SCENE_CHANGE + */ + public static final int CONTROL_AF_SCENE_CHANGE_DETECTED = 1; + + // // Enumeration values for CaptureResult#FLASH_STATE // diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index cfad098c0cd2..b7d7f7d90a31 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2185,6 +2185,24 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Boolean>("android.control.enableZsl", boolean.class); /** + * <p>Whether scene change is detected within AF regions.</p> + * <p>When AF detects a scene change within current AF regions, it will be set to DETECTED. Otherwise, + * it will be set to NOT_DETECTED. This value will remain NOT_DETECTED if afMode is AF_MODE_OFF or + * AF_MODE_EDOF.</p> + * <p><b>Possible values:</b> + * <ul> + * <li>{@link #CONTROL_AF_SCENE_CHANGE_NOT_DETECTED NOT_DETECTED}</li> + * <li>{@link #CONTROL_AF_SCENE_CHANGE_DETECTED DETECTED}</li> + * </ul></p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * @see #CONTROL_AF_SCENE_CHANGE_NOT_DETECTED + * @see #CONTROL_AF_SCENE_CHANGE_DETECTED + */ + @PublicKey + public static final Key<Integer> CONTROL_AF_SCENE_CHANGE = + new Key<Integer>("android.control.afSceneChange", int.class); + + /** * <p>Operation mode for edge * enhancement.</p> * <p>Edge enhancement improves sharpness and details in the captured image. OFF means diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java index bf35a3d6fbd6..5ccf546a55ad 100644 --- a/core/java/android/hardware/location/NanoAppFilter.java +++ b/core/java/android/hardware/location/NanoAppFilter.java @@ -20,7 +20,6 @@ package android.hardware.location; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; /** * @hide @@ -130,6 +129,14 @@ public class NanoAppFilter { (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion())); } + @Override + public String toString() { + return "nanoAppId: 0x" + Long.toHexString(mAppId) + + ", nanoAppVersion: 0x" + Integer.toHexString(mAppVersion) + + ", versionMask: " + mVersionRestrictionMask + + ", vendorMask: " + mAppIdVendorMask; + } + public static final Parcelable.Creator<NanoAppFilter> CREATOR = new Parcelable.Creator<NanoAppFilter>() { public NanoAppFilter createFromParcel(Parcel in) { diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 5620a627df7f..34588614fcc8 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -33,8 +33,6 @@ import java.util.Random; * * This class only supports 48 bits long addresses and does not support 64 bits long addresses. * Instances of this class are immutable. - * - * @hide */ public final class MacAddress implements Parcelable { diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 811091e37da2..948d4cec1003 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1180,7 +1180,7 @@ public abstract class BatteryStats implements Parcelable { public static final class PackageChange { public String mPackageName; public boolean mUpdate; - public int mVersionCode; + public long mVersionCode; } public static final class DailyItem { diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index ea5273296ffd..b7a464544fc7 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -298,7 +298,7 @@ public class Binder implements IBinder { long callingIdentity = clearCallingIdentity(); Throwable throwableToPropagate = null; try { - action.run(); + action.runOrThrow(); } catch (Throwable throwable) { throwableToPropagate = throwable; } finally { @@ -322,7 +322,7 @@ public class Binder implements IBinder { long callingIdentity = clearCallingIdentity(); Throwable throwableToPropagate = null; try { - return action.get(); + return action.getOrThrow(); } catch (Throwable throwable) { throwableToPropagate = throwable; return null; // overridden by throwing in finally block diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index c8c428e9417b..3db12ed0815f 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -55,12 +55,12 @@ interface IStatsManager { * Inform statsd what the version and package are for each uid. Note that each array should * have the same number of elements, and version[i] and package[i] correspond to uid[i]. */ - oneway void informAllUidData(in int[] uid, in int[] version, in String[] app); + oneway void informAllUidData(in int[] uid, in long[] version, in String[] app); /** * Inform statsd what the uid and version are for one app that was updated. */ - oneway void informOnePackage(in String app, in int uid, in int version); + oneway void informOnePackage(in String app, in int uid, in long version); /** * Inform stats that an app was removed. diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 0620fa3dd084..9c90c3802caf 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -79,7 +79,7 @@ interface IUserManager { void setDefaultGuestRestrictions(in Bundle restrictions); Bundle getDefaultGuestRestrictions(); boolean markGuestForDeletion(int userHandle); - void setQuietModeEnabled(int userHandle, boolean enableQuietMode); + void setQuietModeEnabled(int userHandle, boolean enableQuietMode, in IntentSender target); boolean isQuietModeEnabled(int userHandle); boolean trySetQuietModeDisabled(int userHandle, in IntentSender target); void setSeedAccountData(int userHandle, in String accountName, diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index d066db1fc4cc..b303e10fa64b 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -109,7 +109,9 @@ public final class Message implements Parcelable { // sometimes we store linked lists of these things /*package*/ Message next; - private static final Object sPoolSync = new Object(); + + /** @hide */ + public static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; @@ -370,6 +372,12 @@ public final class Message implements Parcelable { return callback; } + /** @hide */ + public Message setCallback(Runnable r) { + callback = r; + return this; + } + /** * Obtains a Bundle of arbitrary data associated with this * event, lazily creating it if necessary. Set this value by calling @@ -411,6 +419,16 @@ public final class Message implements Parcelable { } /** + * Chainable setter for {@link #what} + * + * @hide + */ + public Message setWhat(int what) { + this.what = what; + return this; + } + + /** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a null pointer exception if this field has not been set. */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 22967af7fc59..61dd462f2e07 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2115,7 +2115,7 @@ public class UserManager { */ public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) { try { - mService.setQuietModeEnabled(userHandle, enableQuietMode); + mService.setQuietModeEnabled(userHandle, enableQuietMode, null); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2142,10 +2142,14 @@ public class UserManager { * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled} * directly. * - * @return true if the quiet mode was disabled immediately + * @param userHandle The user that is going to disable quiet mode. + * @param target The target to launch when the user is unlocked. + * @return {@code true} if quiet mode is disabled without showing confirm credentials screen, + * {@code false} otherwise. * @hide */ - public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) { + public boolean trySetQuietModeDisabled( + @UserIdInt int userHandle, @Nullable IntentSender target) { try { return mService.trySetQuietModeDisabled(userHandle, target); } catch (RemoteException re) { diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java index 5a3f39079cf6..a93d1e113eec 100644 --- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java +++ b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java @@ -28,7 +28,7 @@ import android.os.Parcelable; */ public class KeyAttestationPackageInfo implements Parcelable { private final String mPackageName; - private final int mPackageVersionCode; + private final long mPackageVersionCode; private final Signature[] mPackageSignatures; /** @@ -37,7 +37,7 @@ public class KeyAttestationPackageInfo implements Parcelable { * @param mPackageSignatures */ public KeyAttestationPackageInfo( - String mPackageName, int mPackageVersionCode, Signature[] mPackageSignatures) { + String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) { super(); this.mPackageName = mPackageName; this.mPackageVersionCode = mPackageVersionCode; @@ -52,7 +52,7 @@ public class KeyAttestationPackageInfo implements Parcelable { /** * @return the mPackageVersionCode */ - public int getPackageVersionCode() { + public long getPackageVersionCode() { return mPackageVersionCode; } /** @@ -70,7 +70,7 @@ public class KeyAttestationPackageInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); - dest.writeInt(mPackageVersionCode); + dest.writeLong(mPackageVersionCode); dest.writeTypedArray(mPackageSignatures, flags); } @@ -89,7 +89,7 @@ public class KeyAttestationPackageInfo implements Parcelable { private KeyAttestationPackageInfo(Parcel source) { mPackageName = source.readString(); - mPackageVersionCode = source.readInt(); + mPackageVersionCode = source.readLong(); mPackageSignatures = source.createTypedArray(Signature.CREATOR); } } diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java index 5ff0e21078af..8a7ff4da26e3 100644 --- a/services/core/java/com/android/server/notification/ScheduleCalendar.java +++ b/core/java/android/service/notification/ScheduleCalendar.java @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2014, The Android Open Source Project +/* + * 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 + * 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, @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.notification; +package android.service.notification; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.util.ArraySet; @@ -24,7 +24,12 @@ import java.util.Calendar; import java.util.Objects; import java.util.TimeZone; +/** + * @hide + */ public class ScheduleCalendar { + public static final String TAG = "ScheduleCalendar"; + public static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); private final ArraySet<Integer> mDays = new ArraySet<Integer>(); private final Calendar mCalendar = Calendar.getInstance(); @@ -35,12 +40,28 @@ public class ScheduleCalendar { return "ScheduleCalendar[mDays=" + mDays + ", mSchedule=" + mSchedule + "]"; } + /** + * @return true if schedule will exit on alarm, else false + */ + public boolean exitAtAlarm() { + return mSchedule.exitAtAlarm; + } + + /** + * Sets schedule information + */ public void setSchedule(ScheduleInfo schedule) { if (Objects.equals(mSchedule, schedule)) return; mSchedule = schedule; updateDays(); } + /** + * Sets next alarm of the schedule if the saved next alarm has passed or is further + * in the future than given nextAlarm + * @param now current time in milliseconds + * @param nextAlarm time of next alarm in milliseconds + */ public void maybeSetNextAlarm(long now, long nextAlarm) { if (mSchedule != null && mSchedule.exitAtAlarm) { // alarm canceled @@ -56,19 +77,26 @@ public class ScheduleCalendar { mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm); } } else if (mSchedule.nextAlarm < now) { - if (ScheduleConditionProvider.DEBUG) { - Log.d(ScheduleConditionProvider.TAG, - "All alarms are in the past " + mSchedule.nextAlarm); + if (DEBUG) { + Log.d(TAG, "All alarms are in the past " + mSchedule.nextAlarm); } mSchedule.nextAlarm = 0; } } } + /** + * Set calendar time zone to tz + * @param tz current time zone + */ public void setTimeZone(TimeZone tz) { mCalendar.setTimeZone(tz); } + /** + * @param now current time in milliseconds + * @return next time this rule changes (starts or ends) + */ public long getNextChangeTime(long now) { if (mSchedule == null) return 0; final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute); @@ -92,6 +120,10 @@ public class ScheduleCalendar { return mCalendar.getTimeInMillis(); } + /** + * @param time milliseconds since Epoch + * @return true if time is within the schedule, else false + */ public boolean isInSchedule(long time) { if (mSchedule == null || mDays.size() == 0) return false; final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute); @@ -102,6 +134,10 @@ public class ScheduleCalendar { return isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end); } + /** + * @param time milliseconds since Epoch + * @return true if should exit at time for next alarm, else false + */ public boolean shouldExitForAlarm(long time) { if (mSchedule == null) { return false; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 1ec24061eb98..512f2df9747e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -48,6 +48,7 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; +import java.util.TimeZone; import java.util.UUID; /** @@ -692,6 +693,20 @@ public class ZenModeConfig implements Parcelable { suppressedVisualEffects); } + /** + * Creates scheduleCalendar from a condition id + * @param conditionId + * @return ScheduleCalendar with info populated with conditionId + */ + public static ScheduleCalendar toScheduleCalendar(Uri conditionId) { + final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId); + if (schedule == null || schedule.days == null || schedule.days.length == 0) return null; + final ScheduleCalendar sc = new ScheduleCalendar(); + sc.setSchedule(schedule); + sc.setTimeZone(TimeZone.getDefault()); + return sc; + } + private static int sourceToPrioritySenders(int source, int def) { switch (source) { case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY; @@ -793,7 +808,10 @@ public class ZenModeConfig implements Parcelable { Condition.FLAG_RELEVANT_NOW); } - private static CharSequence getFormattedTime(Context context, long time, boolean isSameDay, + /** + * Creates readable time from time in milliseconds + */ + public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay, int userHandle) { String skeleton = (!isSameDay ? "EEE " : "") + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"); @@ -801,7 +819,10 @@ public class ZenModeConfig implements Parcelable { return DateFormat.format(pattern, time); } - private static boolean isToday(long time) { + /** + * Determines whether a time in milliseconds is today or not + */ + public static boolean isToday(long time) { GregorianCalendar now = new GregorianCalendar(); GregorianCalendar endTime = new GregorianCalendar(); endTime.setTimeInMillis(time); @@ -1081,7 +1102,10 @@ public class ZenModeConfig implements Parcelable { return UUID.randomUUID().toString().replace("-", ""); } - private static String getOwnerCaption(Context context, String owner) { + /** + * Gets the name of the app associated with owner + */ + public static String getOwnerCaption(Context context, String owner) { final PackageManager pm = context.getPackageManager(); try { final ApplicationInfo info = pm.getApplicationInfo(owner, 0); diff --git a/core/java/android/text/AutoGrowArray.java b/core/java/android/text/AutoGrowArray.java new file mode 100644 index 000000000000..e428377a0a31 --- /dev/null +++ b/core/java/android/text/AutoGrowArray.java @@ -0,0 +1,374 @@ +/* + * 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 android.text; + +import android.annotation.IntRange; +import android.annotation.NonNull; + +import com.android.internal.util.ArrayUtils; + +import libcore.util.EmptyArray; + +/** + * Implements a growing array of int primitives. + * + * These arrays are NOT thread safe. + * + * @hide + */ +public final class AutoGrowArray { + private static final int MIN_CAPACITY_INCREMENT = 12; + private static final int MAX_CAPACITY_TO_BE_KEPT = 10000; + + /** + * Returns next capacity size. + * + * The returned capacity is larger than requested capacity. + */ + private static int computeNewCapacity(int currentSize, int requested) { + final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) + ? MIN_CAPACITY_INCREMENT : currentSize >> 1); + return targetCapacity > requested ? targetCapacity : requested; + } + + /** + * An auto growing byte array. + */ + public static class ByteArray { + + private @NonNull byte[] mValues; + private @IntRange(from = 0) int mSize; + + /** + * Creates an empty ByteArray with the default initial capacity. + */ + public ByteArray() { + this(10); + } + + /** + * Creates an empty ByteArray with the specified initial capacity. + */ + public ByteArray(@IntRange(from = 0) int initialCapacity) { + if (initialCapacity == 0) { + mValues = EmptyArray.BYTE; + } else { + mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity); + } + mSize = 0; + } + + /** + * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array + * capacity is unchanged. + */ + public void resize(@IntRange(from = 0) int newSize) { + if (newSize > mValues.length) { + ensureCapacity(newSize - mSize); + } + mSize = newSize; + } + + /** + * Appends the specified value to the end of this array. + */ + public void append(byte value) { + ensureCapacity(1); + mValues[mSize++] = value; + } + + /** + * Ensures capacity to append at least <code>count</code> values. + */ + private void ensureCapacity(@IntRange int count) { + final int requestedSize = mSize + count; + if (requestedSize >= mValues.length) { + final int newCapacity = computeNewCapacity(mSize, requestedSize); + final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity); + System.arraycopy(mValues, 0, newValues, 0, mSize); + mValues = newValues; + } + } + + /** + * Removes all values from this array. + */ + public void clear() { + mSize = 0; + } + + /** + * Removes all values from this array and release the internal array object if it is too + * large. + */ + public void clearWithReleasingLargeArray() { + clear(); + if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) { + mValues = EmptyArray.BYTE; + } + } + + /** + * Returns the value at the specified position in this array. + */ + public byte get(@IntRange(from = 0) int index) { + return mValues[index]; + } + + /** + * Sets the value at the specified position in this array. + */ + public void set(@IntRange(from = 0) int index, byte value) { + mValues[index] = value; + } + + /** + * Returns the number of values in this array. + */ + public @IntRange(from = 0) int size() { + return mSize; + } + + /** + * Returns internal raw array. + * + * Note that this array may have larger size than you requested. + * Use size() instead for getting the actual array size. + */ + public @NonNull byte[] getRawArray() { + return mValues; + } + } + + /** + * An auto growing int array. + */ + public static class IntArray { + + private @NonNull int[] mValues; + private @IntRange(from = 0) int mSize; + + /** + * Creates an empty IntArray with the default initial capacity. + */ + public IntArray() { + this(10); + } + + /** + * Creates an empty IntArray with the specified initial capacity. + */ + public IntArray(@IntRange(from = 0) int initialCapacity) { + if (initialCapacity == 0) { + mValues = EmptyArray.INT; + } else { + mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity); + } + mSize = 0; + } + + /** + * Changes the size of this IntArray. If this IntArray is shrinked, the backing array + * capacity is unchanged. + */ + public void resize(@IntRange(from = 0) int newSize) { + if (newSize > mValues.length) { + ensureCapacity(newSize - mSize); + } + mSize = newSize; + } + + /** + * Appends the specified value to the end of this array. + */ + public void append(int value) { + ensureCapacity(1); + mValues[mSize++] = value; + } + + /** + * Ensures capacity to append at least <code>count</code> values. + */ + private void ensureCapacity(@IntRange(from = 0) int count) { + final int requestedSize = mSize + count; + if (requestedSize >= mValues.length) { + final int newCapacity = computeNewCapacity(mSize, requestedSize); + final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity); + System.arraycopy(mValues, 0, newValues, 0, mSize); + mValues = newValues; + } + } + + /** + * Removes all values from this array. + */ + public void clear() { + mSize = 0; + } + + /** + * Removes all values from this array and release the internal array object if it is too + * large. + */ + public void clearWithReleasingLargeArray() { + clear(); + if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) { + mValues = EmptyArray.INT; + } + } + + /** + * Returns the value at the specified position in this array. + */ + public int get(@IntRange(from = 0) int index) { + return mValues[index]; + } + + /** + * Sets the value at the specified position in this array. + */ + public void set(@IntRange(from = 0) int index, int value) { + mValues[index] = value; + } + + /** + * Returns the number of values in this array. + */ + public @IntRange(from = 0) int size() { + return mSize; + } + + /** + * Returns internal raw array. + * + * Note that this array may have larger size than you requested. + * Use size() instead for getting the actual array size. + */ + public @NonNull int[] getRawArray() { + return mValues; + } + } + + /** + * An auto growing float array. + */ + public static class FloatArray { + + private @NonNull float[] mValues; + private @IntRange(from = 0) int mSize; + + /** + * Creates an empty FloatArray with the default initial capacity. + */ + public FloatArray() { + this(10); + } + + /** + * Creates an empty FloatArray with the specified initial capacity. + */ + public FloatArray(@IntRange(from = 0) int initialCapacity) { + if (initialCapacity == 0) { + mValues = EmptyArray.FLOAT; + } else { + mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity); + } + mSize = 0; + } + + /** + * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array + * capacity is unchanged. + */ + public void resize(@IntRange(from = 0) int newSize) { + if (newSize > mValues.length) { + ensureCapacity(newSize - mSize); + } + mSize = newSize; + } + + /** + * Appends the specified value to the end of this array. + */ + public void append(float value) { + ensureCapacity(1); + mValues[mSize++] = value; + } + + /** + * Ensures capacity to append at least <code>count</code> values. + */ + private void ensureCapacity(int count) { + final int requestedSize = mSize + count; + if (requestedSize >= mValues.length) { + final int newCapacity = computeNewCapacity(mSize, requestedSize); + final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity); + System.arraycopy(mValues, 0, newValues, 0, mSize); + mValues = newValues; + } + } + + /** + * Removes all values from this array. + */ + public void clear() { + mSize = 0; + } + + /** + * Removes all values from this array and release the internal array object if it is too + * large. + */ + public void clearWithReleasingLargeArray() { + clear(); + if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) { + mValues = EmptyArray.FLOAT; + } + } + + /** + * Returns the value at the specified position in this array. + */ + public float get(@IntRange(from = 0) int index) { + return mValues[index]; + } + + /** + * Sets the value at the specified position in this array. + */ + public void set(@IntRange(from = 0) int index, float value) { + mValues[index] = value; + } + + /** + * Returns the number of values in this array. + */ + public @IntRange(from = 0) int size() { + return mSize; + } + + /** + * Returns internal raw array. + * + * Note that this array may have larger size than you requested. + * Use size() instead for getting the actual array size. + */ + public @NonNull float[] getRawArray() { + return mValues; + } + } +} diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 4d2a9629c83a..2a693a1841e6 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1907,22 +1907,14 @@ public abstract class Layout { private static float measurePara(TextPaint paint, CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - MeasuredText mt = MeasuredText.obtain(); + MeasuredText mt = null; TextLine tl = TextLine.obtain(); try { - mt.setPara(text, start, end, textDir); - Directions directions; - int dir; - if (mt.mEasy) { - directions = DIRS_ALL_LEFT_TO_RIGHT; - dir = Layout.DIR_LEFT_TO_RIGHT; - } else { - directions = AndroidBidi.directions(mt.mDir, mt.mLevels, - 0, mt.mChars, 0, mt.mLen); - dir = mt.mDir; - } - char[] chars = mt.mChars; - int len = mt.mLen; + mt = MeasuredText.buildForBidi(text, start, end, textDir, mt); + final char[] chars = mt.getChars(); + final int len = chars.length; + final Directions directions = mt.getDirections(0, len); + final int dir = mt.getParagraphDir(); boolean hasTabs = false; TabStops tabStops = null; // leading margins should be taken into account when measuring a paragraph @@ -1955,7 +1947,9 @@ public abstract class Layout { return margin + Math.abs(tl.metrics(null)); } finally { TextLine.recycle(tl); - MeasuredText.recycle(mt); + if (mt != null) { + mt.recycle(); + } } } @@ -2272,6 +2266,11 @@ public abstract class Layout { private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; private int mJustificationMode; + /** @hide */ + @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Direction {} + public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 3d9fba71db74..14d6f9e8a9ef 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -16,125 +16,436 @@ package android.text; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Paint; +import android.text.AutoGrowArray.ByteArray; +import android.text.AutoGrowArray.FloatArray; +import android.text.AutoGrowArray.IntArray; +import android.text.Layout.Directions; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; -import android.util.Log; +import android.util.Pools.SynchronizedPool; -import com.android.internal.util.ArrayUtils; +import dalvik.annotation.optimization.CriticalNative; + +import libcore.util.NativeAllocationRegistry; + +import java.util.Arrays; /** + * MeasuredText provides text information for rendering purpose. + * + * The first motivation of this class is identify the text directions and retrieving individual + * character widths. However retrieving character widths is slower than identifying text directions. + * Thus, this class provides several builder methods for specific purposes. + * + * - buildForBidi: + * Compute only text directions. + * - buildForMeasurement: + * Compute text direction and all character widths. + * - buildForStaticLayout: + * This is bit special. StaticLayout also needs to know text direction and character widths for + * line breaking, but all things are done in native code. Similarly, text measurement is done + * in native code. So instead of storing result to Java array, this keeps the result in native + * code since there is no good reason to move the results to Java layer. + * + * In addition to the character widths, some additional information is computed for each purposes, + * e.g. whole text length for measurement or font metrics for static layout. + * + * MeasuredText is NOT a thread safe object. * @hide */ -class MeasuredText { - private static final boolean localLOGV = false; - CharSequence mText; - int mTextStart; - float[] mWidths; - char[] mChars; - byte[] mLevels; - int mDir; - boolean mEasy; - int mLen; - - private int mPos; - private TextPaint mWorkPaint; - - private MeasuredText() { - mWorkPaint = new TextPaint(); +public class MeasuredText { + private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; + + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024); + + private MeasuredText() {} // Use build static functions instead. + + private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1); + + private static @NonNull MeasuredText obtain() { // Use build static functions instead. + final MeasuredText mt = sPool.acquire(); + return mt != null ? mt : new MeasuredText(); } - private static final Object[] sLock = new Object[0]; - private static final MeasuredText[] sCached = new MeasuredText[3]; - - static MeasuredText obtain() { - MeasuredText mt; - synchronized (sLock) { - for (int i = sCached.length; --i >= 0;) { - if (sCached[i] != null) { - mt = sCached[i]; - sCached[i] = null; - return mt; - } - } + /** + * Recycle the MeasuredText. + * + * Do not call any methods after you call this method. + */ + public void recycle() { + release(); + sPool.release(this); + } + + // The casted original text. + // + // This may be null if the passed text is not a Spanned. + private @Nullable Spanned mSpanned; + + // The start offset of the target range in the original text (mSpanned); + private @IntRange(from = 0) int mTextStart; + + // The length of the target range in the original text. + private @IntRange(from = 0) int mTextLength; + + // The copied character buffer for measuring text. + // + // The length of this array is mTextLength. + private @Nullable char[] mCopiedBuffer; + + // The whole paragraph direction. + private @Layout.Direction int mParaDir; + + // True if the text is LTR direction and doesn't contain any bidi characters. + private boolean mLtrWithoutBidi; + + // The bidi level for individual characters. + // + // This is empty if mLtrWithoutBidi is true. + private @NonNull ByteArray mLevels = new ByteArray(); + + // The whole width of the text. + // See getWholeWidth comments. + private @FloatRange(from = 0.0f) float mWholeWidth; + + // Individual characters' widths. + // See getWidths comments. + private @Nullable FloatArray mWidths = new FloatArray(); + + // The span end positions. + // See getSpanEndCache comments. + private @Nullable IntArray mSpanEndCache = new IntArray(4); + + // The font metrics. + // See getFontMetrics comments. + private @Nullable IntArray mFontMetrics = new IntArray(4 * 4); + + // The native MeasuredText. + // See getNativePtr comments. + // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead. + private /* Maybe Zero */ long mNativePtr = 0; + private @Nullable Runnable mNativeObjectCleaner; + + // Associate the native object to this Java object. + private void bindNativeObject(/* Non Zero*/ long nativePtr) { + mNativePtr = nativePtr; + mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr); + } + + // Decouple the native object from this Java object and release the native object. + private void unbindNativeObject() { + if (mNativePtr != 0) { + mNativeObjectCleaner.run(); + mNativePtr = 0; } - mt = new MeasuredText(); - if (localLOGV) { - Log.v("MEAS", "new: " + mt); + } + + // Following two objects are for avoiding object allocation. + private @NonNull TextPaint mCachedPaint = new TextPaint(); + private @Nullable Paint.FontMetricsInt mCachedFm; + + /** + * Releases internal buffers. + */ + public void release() { + reset(); + mLevels.clearWithReleasingLargeArray(); + mWidths.clearWithReleasingLargeArray(); + mFontMetrics.clearWithReleasingLargeArray(); + mSpanEndCache.clearWithReleasingLargeArray(); + } + + /** + * Resets the internal state for starting new text. + */ + private void reset() { + mSpanned = null; + mCopiedBuffer = null; + mWholeWidth = 0; + mLevels.clear(); + mWidths.clear(); + mFontMetrics.clear(); + mSpanEndCache.clear(); + unbindNativeObject(); + } + + /** + * Returns the characters to be measured. + * + * This is always available. + */ + public @NonNull char[] getChars() { + return mCopiedBuffer; + } + + /** + * Returns the paragraph direction. + * + * This is always available. + */ + public @Layout.Direction int getParagraphDir() { + return mParaDir; + } + + /** + * Returns the directions. + * + * This is always available. + */ + public Directions getDirections(@IntRange(from = 0) int start, // inclusive + @IntRange(from = 0) int end) { // exclusive + if (mLtrWithoutBidi) { + return Layout.DIRS_ALL_LEFT_TO_RIGHT; } + + final int length = end - start; + return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start, + length); + } + + /** + * Returns the whole text width. + * + * This is available only if the MeasureText is computed with computeForMeasurement. + * Returns 0 in other cases. + */ + public @FloatRange(from = 0.0f) float getWholeWidth() { + return mWholeWidth; + } + + /** + * Returns the individual character's width. + * + * This is available only if the MeasureText is computed with computeForMeasurement. + * Returns empty array in other cases. + */ + public @NonNull FloatArray getWidths() { + return mWidths; + } + + /** + * Returns the MetricsAffectingSpan end indices. + * + * If the input text is not a spanned string, this has one value that is the length of the text. + * + * This is available only if the MeasureText is computed with computeForStaticLayout. + * Returns empty array in other cases. + */ + public @NonNull IntArray getSpanEndCache() { + return mSpanEndCache; + } + + /** + * Returns the int array which holds FontMetrics. + * + * This array holds the repeat of top, bottom, ascent, descent of font metrics value. + * + * This is available only if the MeasureText is computed with computeForStaticLayout. + * Returns empty array in other cases. + */ + public @NonNull IntArray getFontMetrics() { + return mFontMetrics; + } + + /** + * Returns the native ptr of the MeasuredText. + * + * This is available only if the MeasureText is computed with computeForStaticLayout. + * Returns 0 in other cases. + */ + public /* Maybe Zero */ long getNativePtr() { + return mNativePtr; + } + + /** + * Generates new MeasuredText for Bidi computation. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param text the character sequence to be measured + * @param start the inclusive start offset of the target region in the text + * @param end the exclusive end offset of the target region in the text + * @param textDir the text direction + * @param recycle pass existing MeasuredText if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredText recycle) { + final MeasuredText mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); return mt; } - static MeasuredText recycle(MeasuredText mt) { - mt.finish(); - synchronized(sLock) { - for (int i = 0; i < sCached.length; ++i) { - if (sCached[i] == null) { - sCached[i] = mt; - mt.mText = null; - break; - } + /** + * Generates new MeasuredText for measuring texts. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param paint the paint to be used for rendering the text. + * @param text the character sequence to be measured + * @param start the inclusive start offset of the target region in the text + * @param end the exclusive end offset of the target region in the text + * @param textDir the text direction + * @param recycle pass existing MeasuredText if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint, + @NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredText recycle) { + final MeasuredText mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); + + mt.mWidths.resize(mt.mTextLength); + if (mt.mTextLength == 0) { + return mt; + } + + if (mt.mSpanned == null) { + // No style change by MetricsAffectingSpan. Just measure all text. + mt.applyMetricsAffectingSpan( + paint, null /* spans */, start, end, 0 /* native static layout ptr */); + } else { + // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. + int spanEnd; + for (int spanStart = start; spanStart < end; spanStart = spanEnd) { + spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); + MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, + MetricAffectingSpan.class); + spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); + mt.applyMetricsAffectingSpan( + paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */); } } - return null; + return mt; } - void finish() { - mText = null; - if (mLen > 1000) { - mWidths = null; - mChars = null; - mLevels = null; + /** + * Generates new MeasuredText for StaticLayout. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param paint the paint to be used for rendering the text. + * @param text the character sequence to be measured + * @param start the inclusive start offset of the target region in the text + * @param end the exclusive end offset of the target region in the text + * @param textDir the text direction + * @param recycle pass existing MeasuredText if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredText buildForStaticLayout( + @NonNull TextPaint paint, + @NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredText recycle) { + final MeasuredText mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); + if (mt.mTextLength == 0) { + // Need to build empty native measured text for StaticLayout. + // TODO: Stop creating empty measured text for empty lines. + long nativeBuilderPtr = nInitBuilder(); + try { + mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer)); + } finally { + nFreeBuilder(nativeBuilderPtr); + } + return mt; + } + + long nativeBuilderPtr = nInitBuilder(); + try { + if (mt.mSpanned == null) { + // No style change by MetricsAffectingSpan. Just measure all text. + mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr); + mt.mSpanEndCache.append(end); + } else { + // There may be a MetricsAffectingSpan. Split into span transitions and apply + // styles. + int spanEnd; + for (int spanStart = start; spanStart < end; spanStart = spanEnd) { + spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, + MetricAffectingSpan.class); + MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, + MetricAffectingSpan.class); + spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, + MetricAffectingSpan.class); + mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, + nativeBuilderPtr); + mt.mSpanEndCache.append(spanEnd); + } + } + mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer)); + } finally { + nFreeBuilder(nativeBuilderPtr); } + + return mt; } /** - * Analyzes text for bidirectional runs. Allocates working buffers. + * Reset internal state and analyzes text for bidirectional runs. + * + * @param text the character sequence to be measured + * @param start the inclusive start offset of the target region in the text + * @param end the exclusive end offset of the target region in the text + * @param textDir the text direction */ - void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - mText = text; + private void resetAndAnalyzeBidi(@NonNull CharSequence text, + @IntRange(from = 0) int start, // inclusive + @IntRange(from = 0) int end, // exclusive + @NonNull TextDirectionHeuristic textDir) { + reset(); + mSpanned = text instanceof Spanned ? (Spanned) text : null; mTextStart = start; + mTextLength = end - start; - int len = end - start; - mLen = len; - mPos = 0; - - if (mWidths == null || mWidths.length < len) { - mWidths = ArrayUtils.newUnpaddedFloatArray(len); - } - if (mChars == null || mChars.length != len) { - mChars = new char[len]; + if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) { + mCopiedBuffer = new char[mTextLength]; } - TextUtils.getChars(text, start, end, mChars, 0); + TextUtils.getChars(text, start, end, mCopiedBuffer, 0); - if (text instanceof Spanned) { - Spanned spanned = (Spanned) text; - ReplacementSpan[] spans = spanned.getSpans(start, end, - ReplacementSpan.class); + // Replace characters associated with ReplacementSpan to U+FFFC. + if (mSpanned != null) { + ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class); for (int i = 0; i < spans.length; i++) { - int startInPara = spanned.getSpanStart(spans[i]) - start; - int endInPara = spanned.getSpanEnd(spans[i]) - start; - // The span interval may be larger and must be restricted to [start, end[ + int startInPara = mSpanned.getSpanStart(spans[i]) - start; + int endInPara = mSpanned.getSpanEnd(spans[i]) - start; + // The span interval may be larger and must be restricted to [start, end) if (startInPara < 0) startInPara = 0; - if (endInPara > len) endInPara = len; - for (int j = startInPara; j < endInPara; j++) { - mChars[j] = '\uFFFC'; // object replacement character - } + if (endInPara > mTextLength) endInPara = mTextLength; + Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER); } } if ((textDir == TextDirectionHeuristics.LTR || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || textDir == TextDirectionHeuristics.ANYRTL_LTR) && - TextUtils.doesNotNeedBidi(mChars, 0, len)) { - mDir = Layout.DIR_LEFT_TO_RIGHT; - mEasy = true; + TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) { + mLevels.clear(); + mParaDir = Layout.DIR_LEFT_TO_RIGHT; + mLtrWithoutBidi = true; } else { - if (mLevels == null || mLevels.length < len) { - mLevels = ArrayUtils.newUnpaddedByteArray(len); - } - int bidiRequest; + final int bidiRequest; if (textDir == TextDirectionHeuristics.LTR) { bidiRequest = Layout.DIR_REQUEST_LTR; } else if (textDir == TextDirectionHeuristics.RTL) { @@ -144,122 +455,147 @@ class MeasuredText { } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL; } else { - boolean isRtl = textDir.isRtl(mChars, 0, len); + final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength); bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR; } - mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels); - mEasy = false; + mLevels.resize(mTextLength); + mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray()); + mLtrWithoutBidi = false; } } - /** - * Apply the style. - * - * If nativeStaticLayoutPtr is 0, this method measures the styled text width. - * If nativeStaticLayoutPtr is not 0, this method just passes the style information to native - * code by calling StaticLayout.addstyleRun() and returns 0. - */ - float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm, - long nativeStaticLayoutPtr) { - if (fm != null) { - paint.getFontMetricsInt(fm); + private void applyReplacementRun(@NonNull ReplacementSpan replacement, + @IntRange(from = 0) int start, // inclusive, in copied buffer + @IntRange(from = 0) int end, // exclusive, in copied buffer + /* Maybe Zero */ long nativeBuilderPtr) { + // Use original text. Shouldn't matter. + // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for + // backward compatibility? or Should we initialize them for getFontMetricsInt? + final float width = replacement.getSize( + mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm); + if (nativeBuilderPtr == 0) { + // Assigns all width to the first character. This is the same behavior as minikin. + mWidths.set(start, width); + if (end > start + 1) { + Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f); + } + mWholeWidth += width; + } else { + nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, + width); } + } - final int p = mPos; - mPos = p + len; + private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer + @IntRange(from = 0) int end, // exclusive, in copied buffer + /* Maybe Zero */ long nativeBuilderPtr) { + if (nativeBuilderPtr != 0) { + mCachedPaint.getFontMetricsInt(mCachedFm); + } - if (mEasy) { - final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT; - if (nativeStaticLayoutPtr == 0) { - return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p); + if (mLtrWithoutBidi) { + // If the whole text is LTR direction, just apply whole region. + if (nativeBuilderPtr == 0) { + mWholeWidth += mCachedPaint.getTextRunAdvances( + mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */, + mWidths.getRawArray(), start); } else { - StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, p, p + len, isRtl); - return 0.0f; // Builder.addStyleRun doesn't return the width. + nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, + false /* isRtl */); } - } - - float totalAdvance = 0; - int level = mLevels[p]; - for (int q = p, i = p + 1, e = p + len;; ++i) { - if (i == e || mLevels[i] != level) { - final boolean isRtl = (level & 0x1) != 0; - if (nativeStaticLayoutPtr == 0) { - totalAdvance += - paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q); - } else { - // Builder.addStyleRun doesn't return the width. - StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, q, i, isRtl); - } - if (i == e) { - break; + } else { + // If there is multiple bidi levels, split into individual bidi level and apply style. + byte level = mLevels.get(start); + // Note that the empty text or empty range won't reach this method. + // Safe to search from start + 1. + for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) { + if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point + final boolean isRtl = (level & 0x1) != 0; + if (nativeBuilderPtr == 0) { + final int levelLength = levelEnd - levelStart; + mWholeWidth += mCachedPaint.getTextRunAdvances( + mCopiedBuffer, levelStart, levelLength, levelStart, levelLength, + isRtl, mWidths.getRawArray(), levelStart); + } else { + nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart, + levelEnd, isRtl); + } + if (levelEnd == end) { + break; + } + levelStart = levelEnd; + level = mLevels.get(levelEnd); } - q = i; - level = mLevels[i]; } } - return totalAdvance; // If nativeStaticLayoutPtr is 0, the result is zero. } - float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) { - return addStyleRun(paint, len, fm, 0 /* native ptr */); - } + private void applyMetricsAffectingSpan( + @NonNull TextPaint paint, + @Nullable MetricAffectingSpan[] spans, + @IntRange(from = 0) int start, // inclusive, in original text buffer + @IntRange(from = 0) int end, // exclusive, in original text buffer + /* Maybe Zero */ long nativeBuilderPtr) { + mCachedPaint.set(paint); + // XXX paint should not have a baseline shift, but... + mCachedPaint.baselineShift = 0; - float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, - Paint.FontMetricsInt fm, long nativeStaticLayoutPtr) { + final boolean needFontMetrics = nativeBuilderPtr != 0; - TextPaint workPaint = mWorkPaint; - workPaint.set(paint); - // XXX paint should not have a baseline shift, but... - workPaint.baselineShift = 0; + if (needFontMetrics && mCachedFm == null) { + mCachedFm = new Paint.FontMetricsInt(); + } ReplacementSpan replacement = null; - for (int i = 0; i < spans.length; i++) { - MetricAffectingSpan span = spans[i]; - if (span instanceof ReplacementSpan) { - replacement = (ReplacementSpan)span; - } else { - span.updateMeasureState(workPaint); + if (spans != null) { + for (int i = 0; i < spans.length; i++) { + MetricAffectingSpan span = spans[i]; + if (span instanceof ReplacementSpan) { + // The last ReplacementSpan is effective for backward compatibility reasons. + replacement = (ReplacementSpan) span; + } else { + // TODO: No need to call updateMeasureState for ReplacementSpan as well? + span.updateMeasureState(mCachedPaint); + } } } - float wid; - if (replacement == null) { - wid = addStyleRun(workPaint, len, fm, nativeStaticLayoutPtr); + final int startInCopiedBuffer = start - mTextStart; + final int endInCopiedBuffer = end - mTextStart; + + if (replacement != null) { + applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, + nativeBuilderPtr); } else { - // Use original text. Shouldn't matter. - wid = replacement.getSize(workPaint, mText, mTextStart + mPos, - mTextStart + mPos + len, fm); - if (nativeStaticLayoutPtr == 0) { - float[] w = mWidths; - w[mPos] = wid; - for (int i = mPos + 1, e = mPos + len; i < e; i++) - w[i] = 0; - } else { - StaticLayout.addReplacementRun(nativeStaticLayoutPtr, paint, mPos, mPos + len, wid); - } - mPos += len; + applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr); } - if (fm != null) { - if (workPaint.baselineShift < 0) { - fm.ascent += workPaint.baselineShift; - fm.top += workPaint.baselineShift; + if (needFontMetrics) { + if (mCachedPaint.baselineShift < 0) { + mCachedFm.ascent += mCachedPaint.baselineShift; + mCachedFm.top += mCachedPaint.baselineShift; } else { - fm.descent += workPaint.baselineShift; - fm.bottom += workPaint.baselineShift; + mCachedFm.descent += mCachedPaint.baselineShift; + mCachedFm.bottom += mCachedPaint.baselineShift; } - } - - return wid; - } - float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, - Paint.FontMetricsInt fm) { - return addStyleRun(paint, spans, len, fm, 0 /* native ptr */); + mFontMetrics.append(mCachedFm.top); + mFontMetrics.append(mCachedFm.bottom); + mFontMetrics.append(mCachedFm.ascent); + mFontMetrics.append(mCachedFm.descent); + } } - int breakText(int limit, boolean forwards, float width) { - float[] w = mWidths; + /** + * Returns the maximum index that the accumulated width not exceeds the width. + * + * If forward=false is passed, returns the minimum index from the end instead. + * + * This only works if the MeasuredText is computed with computeForMeasurement. + * Undefined behavior in other case. + */ + @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) { + float[] w = mWidths.getRawArray(); if (forwards) { int i = 0; while (i < limit) { @@ -267,7 +603,7 @@ class MeasuredText { if (width < 0.0f) break; i++; } - while (i > 0 && mChars[i - 1] == ' ') i--; + while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--; return i; } else { int i = limit - 1; @@ -276,19 +612,65 @@ class MeasuredText { if (width < 0.0f) break; i--; } - while (i < limit - 1 && (mChars[i + 1] == ' ' || w[i + 1] == 0.0f)) { + while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) { i++; } return limit - i - 1; } } - float measure(int start, int limit) { + /** + * Returns the length of the substring. + * + * This only works if the MeasuredText is computed with computeForMeasurement. + * Undefined behavior in other case. + */ + @FloatRange(from = 0.0f) float measure(int start, int limit) { float width = 0; - float[] w = mWidths; + float[] w = mWidths.getRawArray(); for (int i = start; i < limit; ++i) { width += w[i]; } return width; } + + private static native /* Non Zero */ long nInitBuilder(); + + /** + * Apply style to make native measured text. + * + * @param nativeBuilderPtr The native MeasuredText builder pointer. + * @param paintPtr The native paint pointer to be applied. + * @param start The start offset in the copied buffer. + * @param end The end offset in the copied buffer. + * @param isRtl True if the text is RTL. + */ + private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr, + /* Non Zero */ long paintPtr, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + boolean isRtl); + + /** + * Apply ReplacementRun to make native measured text. + * + * @param nativeBuilderPtr The native MeasuredText builder pointer. + * @param paintPtr The native paint pointer to be applied. + * @param start The start offset in the copied buffer. + * @param end The end offset in the copied buffer. + * @param width The width of the replacement. + */ + private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr, + /* Non Zero */ long paintPtr, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @FloatRange(from = 0) float width); + + private static native long nBuildNativeMeasuredText(/* Non Zero */ long nativeBuilderPtr, + @NonNull char[] text); + + private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); + + @CriticalNative + private static native /* Non Zero */ long nGetReleaseFunc(); } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index c0fc44fd8ee1..400b075d889d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -21,10 +21,10 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Paint; +import android.text.AutoGrowArray.FloatArray; import android.text.style.LeadingMarginSpan; import android.text.style.LeadingMarginSpan.LeadingMarginSpan2; import android.text.style.LineHeightSpan; -import android.text.style.MetricAffectingSpan; import android.text.style.TabStopSpan; import android.util.Log; import android.util.Pools.SynchronizedPool; @@ -48,6 +48,18 @@ import java.util.Arrays; * Canvas.drawText()} directly.</p> */ public class StaticLayout extends Layout { + /* + * The break iteration is done in native code. The protocol for using the native code is as + * follows. + * + * First, call nInit to setup native line breaker object. Then, for each paragraph, do the + * following: + * + * - Create MeasuredText by MeasuredText.buildForStaticLayout which measures in native. + * - Run nComputeLineBreaks() to obtain line breaks for the paragraph. + * + * After all paragraphs, call finish() to release expensive buffers. + */ static final String TAG = "StaticLayout"; @@ -99,8 +111,6 @@ public class StaticLayout extends Layout { b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE; - - b.mMeasuredText = MeasuredText.obtain(); return b; } @@ -111,8 +121,6 @@ public class StaticLayout extends Layout { private static void recycle(@NonNull Builder b) { b.mPaint = null; b.mText = null; - MeasuredText.recycle(b.mMeasuredText); - b.mMeasuredText = null; b.mLeftIndents = null; b.mRightIndents = null; b.mLeftPaddings = null; @@ -128,7 +136,6 @@ public class StaticLayout extends Layout { mRightIndents = null; mLeftPaddings = null; mRightPaddings = null; - mMeasuredText.finish(); } public Builder setText(CharSequence source) { @@ -444,9 +451,6 @@ public class StaticLayout extends Layout { private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); - // This will go away and be subsumed by native builder code - private MeasuredText mMeasuredText; - private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3); } @@ -618,11 +622,7 @@ public class StaticLayout extends Layout { TextUtils.TruncateAt ellipsize = b.mEllipsize; final boolean addLastLineSpacing = b.mAddLastLineLineSpacing; LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs - // store span end locations - int[] spanEndCache = new int[4]; - // store fontMetrics per span range - // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) - int[] fmCache = new int[4 * 4]; + FloatArray widths = new FloatArray(); mLineCount = 0; mEllipsized = false; @@ -634,8 +634,6 @@ public class StaticLayout extends Layout { Paint.FontMetricsInt fm = b.mFontMetricsInt; int[] chooseHtv = null; - MeasuredText measured = b.mMeasuredText; - Spanned spanned = null; if (source instanceof Spanned) spanned = (Spanned) source; @@ -662,6 +660,7 @@ public class StaticLayout extends Layout { b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLeftPaddings, mRightPaddings); + MeasuredText measured = null; try { int paraEnd; for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { @@ -721,13 +720,6 @@ public class StaticLayout extends Layout { } } - measured.setPara(source, paraStart, paraEnd, textDir); - char[] chs = measured.mChars; - float[] widths = measured.mWidths; - byte[] chdirs = measured.mLevels; - int dir = measured.mDir; - boolean easy = measured.mEasy; - // tab stop locations int[] variableTabStops = null; if (spanned != null) { @@ -743,56 +735,24 @@ public class StaticLayout extends Layout { } } + measured = MeasuredText.buildForStaticLayout( + paint, source, paraStart, paraEnd, textDir, measured); + final char[] chs = measured.getChars(); + final int[] spanEndCache = measured.getSpanEndCache().getRawArray(); + final int[] fmCache = measured.getFontMetrics().getRawArray(); + // TODO: Stop keeping duplicated width copy in native and Java. + widths.resize(chs.length); + // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the // second time, so we cache those and then use those stored values - int fmCacheCount = 0; - int spanEndCacheCount = 0; - for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { - if (fmCacheCount * 4 >= fmCache.length) { - int[] grow = new int[fmCacheCount * 4 * 2]; - System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4); - fmCache = grow; - } - - if (spanEndCacheCount >= spanEndCache.length) { - int[] grow = new int[spanEndCacheCount * 2]; - System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount); - spanEndCache = grow; - } - - if (spanned == null) { - spanEnd = paraEnd; - int spanLen = spanEnd - spanStart; - measured.addStyleRun(paint, spanLen, fm, nativePtr); - } else { - spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, - MetricAffectingSpan.class); - int spanLen = spanEnd - spanStart; - MetricAffectingSpan[] spans = - spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); - spans = TextUtils.removeEmptySpans(spans, spanned, - MetricAffectingSpan.class); - measured.addStyleRun(paint, spans, spanLen, fm, nativePtr); - } - - // the order of storage here (top, bottom, ascent, descent) has to match the - // code below where these values are retrieved - fmCache[fmCacheCount * 4 + 0] = fm.top; - fmCache[fmCacheCount * 4 + 1] = fm.bottom; - fmCache[fmCacheCount * 4 + 2] = fm.ascent; - fmCache[fmCacheCount * 4 + 3] = fm.descent; - fmCacheCount++; - - spanEndCache[spanEndCacheCount] = spanEnd; - spanEndCacheCount++; - } int breakCount = nComputeLineBreaks( nativePtr, // Inputs chs, + measured.getNativePtr(), paraEnd - paraStart, firstWidth, firstWidthLineCount, @@ -809,7 +769,7 @@ public class StaticLayout extends Layout { lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags, - widths); + widths.getRawArray()); final int[] breaks = lineBreaks.breaks; final float[] lineWidths = lineBreaks.widths; @@ -832,7 +792,7 @@ public class StaticLayout extends Layout { width += lineWidths[i]; } else { for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { - width += widths[j]; + width += widths.get(j); } } flag |= flags[i] & TAB_MASK; @@ -896,10 +856,10 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, - flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, - includepad, trackpad, addLastLineSpacing, chs, widths, paraStart, - ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, - moreChars); + flags[breakIndex], needMultiply, measured, bufEnd, + includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(), + paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], + paint, moreChars); if (endPos < spanEnd) { // preserve metrics for current span @@ -927,22 +887,22 @@ public class StaticLayout extends Layout { if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) { - measured.setPara(source, bufEnd, bufEnd, textDir); - paint.getFontMetricsInt(fm); - v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, - needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, + needMultiply, null, bufEnd, includepad, trackpad, addLastLineSpacing, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false); } } finally { + if (measured != null) { + measured.recycle(); + } nFinish(nativePtr); } } @@ -952,8 +912,8 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, - final int flags, final boolean needMultiply, final byte[] chdirs, final int dir, - final boolean easy, final int bufEnd, final boolean includePad, final boolean trackPad, + final int flags, final boolean needMultiply, @Nullable final MeasuredText measured, + final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final float[] widths, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, final float textWidth, final TextPaint paint, final boolean moreChars) { @@ -961,6 +921,7 @@ public class StaticLayout extends Layout { final int off = j * mColumns; final int want = off + mColumns + TOP; int[] lines = mLines; + final int dir = (start == end) ? Layout.DIR_LEFT_TO_RIGHT : measured.getParagraphDir(); if (want >= lines.length) { final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want)); @@ -986,16 +947,11 @@ public class StaticLayout extends Layout { // one bit for start field lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; - lines[off + DIR] |= dir << DIR_SHIFT; - // easy means all chars < the first RTL, so no emoji, no nothing - // XXX a run with no text or all spaces is easy but might be an empty - // RTL paragraph. Make sure easy is false if this is the case. - if (easy) { - mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT; + if (start == end) { + mLineDirections[j] = Layout.DIRS_ALL_LEFT_TO_RIGHT; } else { - mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs, - start - widthStart, end - start); + mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); } final boolean firstLine = (j == 0); @@ -1473,33 +1429,6 @@ public class StaticLayout extends Layout { mMaxLineHeight : super.getHeight(); } - /** - * Measurement and break iteration is done in native code. The protocol for using - * the native code is as follows. - * - * First, call nInit to setup native line breaker object. Then, for each paragraph, do the - * following: - * - * - Call one of the following methods for each run within the paragraph depending on the type - * of run: - * + addStyleRun (a text run, to be measured in native code) - * + addReplacementRun (a replacement run, width is given) - * - * - Run nComputeLineBreaks() to obtain line breaks for the paragraph. - * - * After all paragraphs, call finish() to release expensive buffers. - */ - - /* package */ static void addStyleRun(long nativePtr, TextPaint paint, int start, int end, - boolean isRtl) { - nAddStyleRun(nativePtr, paint.getNativeInstance(), start, end, isRtl); - } - - /* package */ static void addReplacementRun(long nativePtr, TextPaint paint, int start, int end, - float width) { - nAddReplacementRun(nativePtr, paint.getNativeInstance(), start, end, width); - } - @FastNative private static native long nInit( @BreakStrategy int breakStrategy, @@ -1512,17 +1441,6 @@ public class StaticLayout extends Layout { @CriticalNative private static native void nFinish(long nativePtr); - @CriticalNative - private static native void nAddStyleRun( - /* non-zero */ long nativePtr, /* non-zero */ long nativePaint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl); - - @CriticalNative - private static native void nAddReplacementRun( - /* non-zero */ long nativePtr, /* non-zero */ long nativePaint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, - @FloatRange(from = 0.0f) float width); - // populates LineBreaks and returns the number of breaks found // // the arrays inside the LineBreaks objects are passed in as well @@ -1535,6 +1453,7 @@ public class StaticLayout extends Layout { // Inputs @NonNull char[] text, + /* Non Zero */ long measuredTextPtr, @IntRange(from = 0) int length, @FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount, diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index cbdaa69b5aa0..9c9fbf23832f 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -42,7 +42,6 @@ import android.text.style.EasyEditSpan; import android.text.style.ForegroundColorSpan; import android.text.style.LeadingMarginSpan; import android.text.style.LocaleSpan; -import android.text.style.MetricAffectingSpan; import android.text.style.ParagraphStyle; import android.text.style.QuoteSpan; import android.text.style.RelativeSizeSpan; @@ -1251,10 +1250,11 @@ public class TextUtils { @NonNull String ellipsis) { final int len = text.length(); - final MeasuredText mt = MeasuredText.obtain(); + MeasuredText mt = null; MeasuredText resultMt = null; try { - float width = setPara(mt, paint, text, 0, text.length(), textDir); + mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt); + float width = mt.getWholeWidth(); if (width <= avail) { if (callback != null) { @@ -1263,7 +1263,6 @@ public class TextUtils { return text; } - resultMt = MeasuredText.obtain(); // First estimate of effective width of ellipsis. float ellipsisWidth = paint.measureText(ellipsis); int numberOfTries = 0; @@ -1290,7 +1289,7 @@ public class TextUtils { } } - final char[] buf = mt.mChars; + final char[] buf = mt.getChars(); final Spanned sp = text instanceof Spanned ? (Spanned) text : null; final int removed = end - start; @@ -1333,7 +1332,9 @@ public class TextUtils { if (remaining == 0) { // All text is gone. textFits = true; } else { - width = setPara(resultMt, paint, result, 0, result.length(), textDir); + resultMt = MeasuredText.buildForMeasurement( + paint, result, 0, result.length(), textDir, resultMt); + width = resultMt.getWholeWidth(); if (width <= avail) { textFits = true; } else { @@ -1357,9 +1358,11 @@ public class TextUtils { } return result; } finally { - MeasuredText.recycle(mt); + if (mt != null) { + mt.recycle(); + } if (resultMt != null) { - MeasuredText.recycle(resultMt); + resultMt.recycle(); } } } @@ -1476,15 +1479,17 @@ public class TextUtils { public static CharSequence commaEllipsize(CharSequence text, TextPaint p, float avail, String oneMore, String more, TextDirectionHeuristic textDir) { - MeasuredText mt = MeasuredText.obtain(); + MeasuredText mt = null; + MeasuredText tempMt = null; try { int len = text.length(); - float width = setPara(mt, p, text, 0, len, textDir); + mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt); + final float width = mt.getWholeWidth(); if (width <= avail) { return text; } - char[] buf = mt.mChars; + char[] buf = mt.getChars(); int commaCount = 0; for (int i = 0; i < len; i++) { @@ -1500,9 +1505,8 @@ public class TextUtils { int w = 0; int count = 0; - float[] widths = mt.mWidths; + float[] widths = mt.getWidths().getRawArray(); - MeasuredText tempMt = MeasuredText.obtain(); for (int i = 0; i < len; i++) { w += widths[i]; @@ -1519,8 +1523,9 @@ public class TextUtils { } // XXX this is probably ok, but need to look at it more - tempMt.setPara(format, 0, format.length(), textDir); - float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null); + tempMt = MeasuredText.buildForMeasurement( + p, format, 0, format.length(), textDir, tempMt); + float moreWid = tempMt.getWholeWidth(); if (w + moreWid <= avail) { ok = i + 1; @@ -1528,40 +1533,18 @@ public class TextUtils { } } } - MeasuredText.recycle(tempMt); SpannableStringBuilder out = new SpannableStringBuilder(okFormat); out.insert(0, text, 0, ok); return out; } finally { - MeasuredText.recycle(mt); - } - } - - private static float setPara(MeasuredText mt, TextPaint paint, - CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - - mt.setPara(text, start, end, textDir); - - float width; - Spanned sp = text instanceof Spanned ? (Spanned) text : null; - int len = end - start; - if (sp == null) { - width = mt.addStyleRun(paint, len, null); - } else { - width = 0; - int spanEnd; - for (int spanStart = 0; spanStart < len; spanStart = spanEnd) { - spanEnd = sp.nextSpanTransition(spanStart, len, - MetricAffectingSpan.class); - MetricAffectingSpan[] spans = sp.getSpans( - spanStart, spanEnd, MetricAffectingSpan.class); - spans = TextUtils.removeEmptySpans(spans, sp, MetricAffectingSpan.class); - width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null); + if (mt != null) { + mt.recycle(); + } + if (tempMt != null) { + tempMt.recycle(); } } - - return width; } // Returns true if the character's presence could affect RTL layout. diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java index 70581be80dce..f0b7e01dae48 100644 --- a/core/java/android/util/Pools.java +++ b/core/java/android/util/Pools.java @@ -130,22 +130,29 @@ public final class Pools { } /** - * Synchronized) pool of objects. + * Synchronized pool of objects. * * @param <T> The pooled type. */ public static class SynchronizedPool<T> extends SimplePool<T> { - private final Object mLock = new Object(); + private final Object mLock; /** * Creates a new instance. * * @param maxPoolSize The max pool size. + * @param lock an optional custom object to synchronize on * * @throws IllegalArgumentException If the max pool size is less than zero. */ - public SynchronizedPool(int maxPoolSize) { + public SynchronizedPool(int maxPoolSize, Object lock) { super(maxPoolSize); + mLock = lock; + } + + /** @see #SynchronizedPool(int, Object) */ + public SynchronizedPool(int maxPoolSize) { + this(maxPoolSize, new Object()); } @Override diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 37550d87adf3..420a1bb34d28 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -26,6 +26,8 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; import android.util.Log; @@ -39,12 +41,14 @@ import java.io.Closeable; * SurfaceControl * @hide */ -public class SurfaceControl { +public class SurfaceControl implements Parcelable { private static final String TAG = "SurfaceControl"; private static native long nativeCreate(SurfaceSession session, String name, int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid) throws OutOfResourcesException; + private static native long nativeReadFromParcel(Parcel in); + private static native void nativeWriteToParcel(long nativeObject, Parcel out); private static native void nativeRelease(long nativeObject); private static native void nativeDestroy(long nativeObject); private static native void nativeDisconnect(long nativeObject); @@ -577,6 +581,37 @@ public class SurfaceControl { mCloseGuard.open("release"); } + private SurfaceControl(Parcel in) { + mName = in.readString(); + mNativeObject = nativeReadFromParcel(in); + if (mNativeObject == 0) { + throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in); + } + mCloseGuard.open("release"); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + nativeWriteToParcel(mNativeObject, dest); + } + + public static final Creator<SurfaceControl> CREATOR + = new Creator<SurfaceControl>() { + public SurfaceControl createFromParcel(Parcel in) { + return new SurfaceControl(in); + } + + public SurfaceControl[] newArray(int size) { + return new SurfaceControl[size]; + } + }; + @Override protected void finalize() throws Throwable { try { @@ -667,7 +702,7 @@ public class SurfaceControl { */ @Deprecated public static void mergeToGlobalTransaction(Transaction t) { - synchronized(sGlobalTransaction) { + synchronized(SurfaceControl.class) { sGlobalTransaction.merge(t); } } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index f0645b895afa..c69543f6d2d8 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -67,6 +67,11 @@ public final class InputMethodInfo implements Parcelable { final ResolveInfo mService; /** + * IME only supports VR mode. + */ + final boolean mIsVrOnly; + + /** * The unique string Id to identify the input method. This is generated * from the input method component. */ @@ -149,6 +154,7 @@ public final class InputMethodInfo implements Parcelable { PackageManager pm = context.getPackageManager(); String settingsActivityComponent = null; + boolean isVrOnly; int isDefaultResId = 0; XmlResourceParser parser = null; @@ -179,6 +185,7 @@ public final class InputMethodInfo implements Parcelable { com.android.internal.R.styleable.InputMethod); settingsActivityComponent = sa.getString( com.android.internal.R.styleable.InputMethod_settingsActivity); + isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false); isDefaultResId = sa.getResourceId( com.android.internal.R.styleable.InputMethod_isDefault, 0); supportsSwitchingToNextInputMethod = sa.getBoolean( @@ -254,6 +261,8 @@ public final class InputMethodInfo implements Parcelable { mIsDefaultResId = isDefaultResId; mIsAuxIme = isAuxIme; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; + // TODO(b/68948291): remove this meta-data before release. + mIsVrOnly = isVrOnly || service.serviceInfo.metaData.getBoolean("isVrOnly", false); } InputMethodInfo(Parcel source) { @@ -262,6 +271,7 @@ public final class InputMethodInfo implements Parcelable { mIsDefaultResId = source.readInt(); mIsAuxIme = source.readInt() == 1; mSupportsSwitchingToNextInputMethod = source.readInt() == 1; + mIsVrOnly = source.readBoolean(); mService = ResolveInfo.CREATOR.createFromParcel(source); mSubtypes = new InputMethodSubtypeArray(source); mForceDefault = false; @@ -274,7 +284,8 @@ public final class InputMethodInfo implements Parcelable { CharSequence label, String settingsActivity) { this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, - false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */); + false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, + false /* isVrOnly */); } /** @@ -285,7 +296,7 @@ public final class InputMethodInfo implements Parcelable { String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, - true /* supportsSwitchingToNextInputMethod */); + true /* supportsSwitchingToNextInputMethod */, false /* isVrOnly */); } /** @@ -294,7 +305,7 @@ public final class InputMethodInfo implements Parcelable { */ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, - boolean supportsSwitchingToNextInputMethod) { + boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { final ServiceInfo si = ri.serviceInfo; mService = ri; mId = new ComponentName(si.packageName, si.name).flattenToShortString(); @@ -304,6 +315,7 @@ public final class InputMethodInfo implements Parcelable { mSubtypes = new InputMethodSubtypeArray(subtypes); mForceDefault = forceDefault; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; + mIsVrOnly = isVrOnly; } private static ResolveInfo buildDummyResolveInfo(String packageName, String className, @@ -398,6 +410,14 @@ public final class InputMethodInfo implements Parcelable { } /** + * Returns true if IME supports VR mode only. + * @hide + */ + public boolean isVrOnly() { + return mIsVrOnly; + } + + /** * Return the count of the subtypes of Input Method. */ public int getSubtypeCount() { @@ -444,6 +464,7 @@ public final class InputMethodInfo implements Parcelable { public void dump(Printer pw, String prefix) { pw.println(prefix + "mId=" + mId + " mSettingsActivityName=" + mSettingsActivityName + + " mIsVrOnly=" + mIsVrOnly + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod); pw.println(prefix + "mIsDefaultResId=0x" + Integer.toHexString(mIsDefaultResId)); @@ -509,6 +530,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeInt(mIsDefaultResId); dest.writeInt(mIsAuxIme ? 1 : 0); dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0); + dest.writeBoolean(mIsVrOnly); mService.writeToParcel(dest, flags); mSubtypes.writeToParcel(dest); } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index b2cab5bf4a70..7ffbf6357f45 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -109,8 +109,7 @@ public final class TextClassification { @NonNull private final List<Intent> mSecondaryIntents; @NonNull private final List<OnClickListener> mSecondaryOnClickListeners; @NonNull private final EntityConfidence<String> mEntityConfidence; - private int mLogType; - @NonNull private final String mVersionInfo; + @NonNull private final String mSignature; private TextClassification( @Nullable String text, @@ -123,8 +122,7 @@ public final class TextClassification { @NonNull List<Intent> secondaryIntents, @NonNull List<OnClickListener> secondaryOnClickListeners, @NonNull Map<String, Float> entityConfidence, - int logType, - @NonNull String versionInfo) { + @NonNull String signature) { Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size()); Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size()); Preconditions.checkArgument(secondaryOnClickListeners.size() == secondaryIntents.size()); @@ -138,8 +136,7 @@ public final class TextClassification { mSecondaryIntents = secondaryIntents; mSecondaryOnClickListeners = secondaryOnClickListeners; mEntityConfidence = new EntityConfidence<>(entityConfidence); - mLogType = logType; - mVersionInfo = versionInfo; + mSignature = signature; } /** @@ -315,30 +312,26 @@ public final class TextClassification { } /** - * Returns the MetricsLogger subtype for the action that is performed for this result. - * @hide - */ - public int getLogType() { - return mLogType; - } - - /** - * Returns information about the classifier model used to generate this TextClassification. - * @hide + * Returns the signature for this object. + * The TextClassifier that generates this object may use it as a way to internally identify + * this object. */ @NonNull - public String getVersionInfo() { - return mVersionInfo; + public String getSignature() { + return mSignature; } @Override public String toString() { - return String.format("TextClassification {" + return String.format(Locale.US, "TextClassification {" + "text=%s, entities=%s, " + "primaryLabel=%s, secondaryLabels=%s, " - + "primaryIntent=%s, secondaryIntents=%s}", + + "primaryIntent=%s, secondaryIntents=%s, " + + "signature=%s}", mText, mEntityConfidence, - mPrimaryLabel, mSecondaryLabels, mPrimaryIntent, mSecondaryIntents); + mPrimaryLabel, mSecondaryLabels, + mPrimaryIntent, mSecondaryIntents, + mSignature); } /** @@ -383,8 +376,7 @@ public final class TextClassification { @Nullable String mPrimaryLabel; @Nullable Intent mPrimaryIntent; @Nullable OnClickListener mPrimaryOnClickListener; - private int mLogType; - @NonNull private String mVersionInfo = ""; + @NonNull private String mSignature = ""; /** * Sets the classified text. @@ -508,20 +500,12 @@ public final class TextClassification { } /** - * Sets the MetricsLogger subtype for the action that is performed for this result. - * @hide - */ - public Builder setLogType(int type) { - mLogType = type; - return this; - } - - /** - * Sets information about the classifier model used to generate this TextClassification. - * @hide + * Sets a signature for the TextClassification object. + * The TextClassifier that generates the TextClassification object may use it as a way to + * internally identify the TextClassification object. */ - Builder setVersionInfo(@NonNull String versionInfo) { - mVersionInfo = Preconditions.checkNotNull(versionInfo); + public Builder setSignature(@NonNull String signature) { + mSignature = Preconditions.checkNotNull(signature); return this; } @@ -535,7 +519,7 @@ public final class TextClassification { mPrimaryIntent, mPrimaryOnClickListener, mSecondaryIcons, mSecondaryLabels, mSecondaryIntents, mSecondaryOnClickListeners, - mEntityConfidence, mLogType, mVersionInfo); + mEntityConfidence, mSignature); } } diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index 5aaa5ad1eefe..f4cbc54b452b 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -37,7 +37,7 @@ import java.lang.annotation.RetentionPolicy; public interface TextClassifier { /** @hide */ - String DEFAULT_LOG_TAG = "TextClassifierImpl"; + String DEFAULT_LOG_TAG = "androidtc"; String TYPE_UNKNOWN = ""; String TYPE_OTHER = "other"; diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index d8ea89a2abc7..6cf6b69f4aa6 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -33,7 +33,6 @@ import android.provider.Settings; import android.text.util.Linkify; import android.util.Patterns; import android.view.View.OnClickListener; -import android.widget.TextViewMetrics; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -122,8 +121,8 @@ final class TextClassifierImpl implements TextClassifier { tsBuilder.setEntityType(results[i].mCollection, results[i].mScore); } return tsBuilder - .setLogSource(LOG_TAG) - .setVersionInfo(getVersionInfo()) + .setSignature( + getSignature(string, selectionStartIndex, selectionEndIndex)) .build(); } else { // We can not trust the result. Log the issue and ignore the result. @@ -155,8 +154,7 @@ final class TextClassifierImpl implements TextClassifier { getHintFlags(string, startIndex, endIndex)); if (results.length > 0) { final TextClassification classificationResult = - createClassificationResult( - results, string.subSequence(startIndex, endIndex)); + createClassificationResult(results, string, startIndex, endIndex); return classificationResult; } } @@ -230,13 +228,13 @@ final class TextClassifierImpl implements TextClassifier { } } - @NonNull - private String getVersionInfo() { + private String getSignature(String text, int start, int end) { synchronized (mSmartSelectionLock) { - if (mLocale != null) { - return String.format("%s_v%d", mLocale.toLanguageTag(), mVersion); - } - return ""; + final String versionInfo = (mLocale != null) + ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion) + : ""; + final int hash = Objects.hash(text, start, end, mContext.getPackageName()); + return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash); } } @@ -372,9 +370,11 @@ final class TextClassifierImpl implements TextClassifier { } private TextClassification createClassificationResult( - SmartSelection.ClassificationResult[] classifications, CharSequence text) { + SmartSelection.ClassificationResult[] classifications, + String text, int start, int end) { + final String classifiedText = text.substring(start, end); final TextClassification.Builder builder = new TextClassification.Builder() - .setText(text.toString()); + .setText(classifiedText); final int size = classifications.length; for (int i = 0; i < size; i++) { @@ -382,11 +382,9 @@ final class TextClassifierImpl implements TextClassifier { } final String type = getHighestScoringType(classifications); - builder.setLogType(IntentFactory.getLogType(type)); + addActions(builder, IntentFactory.create(mContext, type, text)); - addActions(builder, IntentFactory.create(mContext, type, text.toString())); - - return builder.setVersionInfo(getVersionInfo()).build(); + return builder.setSignature(getSignature(text, start, end)).build(); } /** Extends the classification with the intents that can be resolved. */ @@ -564,22 +562,5 @@ final class TextClassifierImpl implements TextClassifier { return null; } } - - @Nullable - public static int getLogType(String type) { - type = type.trim().toLowerCase(Locale.ENGLISH); - switch (type) { - case TextClassifier.TYPE_EMAIL: - return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_EMAIL; - case TextClassifier.TYPE_PHONE: - return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_PHONE; - case TextClassifier.TYPE_ADDRESS: - return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_ADDRESS; - case TextClassifier.TYPE_URL: - return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_URL; - default: - return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_OTHER; - } - } } } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index ced4018bcd82..25e9e7ecf0e7 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -37,17 +37,15 @@ public final class TextSelection { private final int mStartIndex; private final int mEndIndex; @NonNull private final EntityConfidence<String> mEntityConfidence; - @NonNull private final String mLogSource; - @NonNull private final String mVersionInfo; + @NonNull private final String mSignature; private TextSelection( - int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence, - @NonNull String logSource, @NonNull String versionInfo) { + int startIndex, int endIndex, @NonNull Map<String, Float> entityConfidence, + @NonNull String signature) { mStartIndex = startIndex; mEndIndex = endIndex; mEntityConfidence = new EntityConfidence<>(entityConfidence); - mLogSource = logSource; - mVersionInfo = versionInfo; + mSignature = signature; } /** @@ -95,27 +93,21 @@ public final class TextSelection { } /** - * Returns a tag for the source classifier used to generate this result. - * @hide + * Returns the signature for this object. + * The TextClassifier that generates this object may use it as a way to internally identify + * this object. */ @NonNull - public String getSourceClassifier() { - return mLogSource; - } - - /** - * Returns information about the classifier model used to generate this TextSelection. - * @hide - */ - @NonNull - public String getVersionInfo() { - return mVersionInfo; + public String getSignature() { + return mSignature; } @Override public String toString() { - return String.format(Locale.US, - "TextSelection {%d, %d, %s}", mStartIndex, mEndIndex, mEntityConfidence); + return String.format( + Locale.US, + "TextSelection {startIndex=%d, endIndex=%d, entities=%s, signature=%s}", + mStartIndex, mEndIndex, mEntityConfidence, mSignature); } /** @@ -126,8 +118,7 @@ public final class TextSelection { private final int mStartIndex; private final int mEndIndex; @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>(); - @NonNull private String mLogSource = ""; - @NonNull private String mVersionInfo = ""; + @NonNull private String mSignature = ""; /** * Creates a builder used to build {@link TextSelection} objects. @@ -157,20 +148,13 @@ public final class TextSelection { } /** - * Sets a tag for the source classifier used to generate this result. - * @hide - */ - Builder setLogSource(@NonNull String logSource) { - mLogSource = Preconditions.checkNotNull(logSource); - return this; - } - - /** - * Sets information about the classifier model used to generate this TextSelection. - * @hide + * Sets a signature for the TextSelection object. + * + * The TextClassifier that generates the TextSelection object may use it as a way to + * internally identify the TextSelection object. */ - Builder setVersionInfo(@NonNull String versionInfo) { - mVersionInfo = Preconditions.checkNotNull(versionInfo); + public Builder setSignature(@NonNull String signature) { + mSignature = Preconditions.checkNotNull(signature); return this; } @@ -179,8 +163,7 @@ public final class TextSelection { */ public TextSelection build() { return new TextSelection( - mStartIndex, mEndIndex, new EntityConfidence<>(mEntityConfidence), mLogSource, - mVersionInfo); + mStartIndex, mEndIndex, mEntityConfidence, mSignature); } } diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java index 2833564f73c4..157b3d82163b 100644 --- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java +++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java @@ -473,7 +473,7 @@ public final class SmartSelectionEventTracker { final String entityType = classification.getEntityCount() > 0 ? classification.getEntity(0) : TextClassifier.TYPE_UNKNOWN; - final String versionTag = classification.getVersionInfo(); + final String versionTag = getVersionInfo(classification.getSignature()); return new SelectionEvent( start, end, EventType.SELECTION_MODIFIED, entityType, versionTag); } @@ -489,7 +489,7 @@ public final class SmartSelectionEventTracker { */ public static SelectionEvent selectionModified( int start, int end, @NonNull TextSelection selection) { - final boolean smartSelection = selection.getSourceClassifier() + final boolean smartSelection = getSourceClassifier(selection.getSignature()) .equals(TextClassifier.DEFAULT_LOG_TAG); final int eventType; if (smartSelection) { @@ -503,7 +503,7 @@ public final class SmartSelectionEventTracker { final String entityType = selection.getEntityCount() > 0 ? selection.getEntity(0) : TextClassifier.TYPE_UNKNOWN; - final String versionTag = selection.getVersionInfo(); + final String versionTag = getVersionInfo(selection.getSignature()); return new SelectionEvent(start, end, eventType, entityType, versionTag); } @@ -538,26 +538,25 @@ public final class SmartSelectionEventTracker { final String entityType = classification.getEntityCount() > 0 ? classification.getEntity(0) : TextClassifier.TYPE_UNKNOWN; - final String versionTag = classification.getVersionInfo(); + final String versionTag = getVersionInfo(classification.getSignature()); return new SelectionEvent(start, end, actionType, entityType, versionTag); } - private boolean isActionType() { - switch (mEventType) { - case ActionType.OVERTYPE: // fall through - case ActionType.COPY: // fall through - case ActionType.PASTE: // fall through - case ActionType.CUT: // fall through - case ActionType.SHARE: // fall through - case ActionType.SMART_SHARE: // fall through - case ActionType.DRAG: // fall through - case ActionType.ABANDON: // fall through - case ActionType.SELECT_ALL: // fall through - case ActionType.RESET: // fall through - return true; - default: - return false; + private static String getVersionInfo(String signature) { + final int start = signature.indexOf("|"); + final int end = signature.indexOf("|", start); + if (start >= 0 && end >= start) { + return signature.substring(start, end); + } + return ""; + } + + private static String getSourceClassifier(String signature) { + final int end = signature.indexOf("|"); + if (end >= 0) { + return signature.substring(0, end); } + return ""; } private boolean isTerminal() { diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 797bdfb705cb..9db0e8d9a2fe 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -265,10 +265,10 @@ public final class WebViewFactory { + "packageName mismatch, expected: " + chosen.packageName + " actual: " + toUse.packageName); } - if (chosen.versionCode > toUse.versionCode) { + if (chosen.getLongVersionCode() > toUse.getLongVersionCode()) { throw new MissingWebViewPackageException("Failed to verify WebView provider, " - + "version code is lower than expected: " + chosen.versionCode - + " actual: " + toUse.versionCode); + + "version code is lower than expected: " + chosen.getLongVersionCode() + + " actual: " + toUse.getLongVersionCode()); } if (getWebViewLibrary(toUse.applicationInfo) == null) { throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: " @@ -401,7 +401,7 @@ public final class WebViewFactory { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " + - sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")"); + sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")"); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); try { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index d4bac9880c1a..df971128b49d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -41,7 +41,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.metrics.LogMaker; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; @@ -3997,10 +3996,6 @@ public class Editor { .setIntent(textClassification.getIntent()); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); mAssistClickHandlers.put(item, textClassification.getOnClickListener()); - mMetricsLogger.write( - new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) - .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(textClassification.getLogType())); } final int count = textClassification.getSecondaryActionsCount(); for (int i = 0; i < count; i++) { @@ -4082,11 +4077,6 @@ public class Editor { if (onClickListener != null) { onClickListener.onClick(mTextView); stopTextActionMode(); - if (assistMenuItem.getItemId() == TextView.ID_ASSIST) { - mMetricsLogger.action( - MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, - textClassification.getLogType()); - } } // We tried our best. return true; diff --git a/core/java/android/widget/TextViewMetrics.java b/core/java/android/widget/TextViewMetrics.java index 96d17943bf09..738a5742c6ba 100644 --- a/core/java/android/widget/TextViewMetrics.java +++ b/core/java/android/widget/TextViewMetrics.java @@ -37,29 +37,4 @@ public final class TextViewMetrics { * Long press on TextView - drag and drop started. */ public static final int SUBTYPE_LONG_PRESS_DRAG_AND_DROP = 2; - - /** - * Assist menu item (shown or clicked) - classification: other. - */ - public static final int SUBTYPE_ASSIST_MENU_ITEM_OTHER = 0; - - /** - * Assist menu item (shown or clicked) - classification: email. - */ - public static final int SUBTYPE_ASSIST_MENU_ITEM_EMAIL = 1; - - /** - * Assist menu item (shown or clicked) - classification: phone. - */ - public static final int SUBTYPE_ASSIST_MENU_ITEM_PHONE = 2; - - /** - * Assist menu item (shown or clicked) - classification: address. - */ - public static final int SUBTYPE_ASSIST_MENU_ITEM_ADDRESS = 3; - - /** - * Assist menu item (shown or clicked) - classification: url. - */ - public static final int SUBTYPE_ASSIST_MENU_ITEM_URL = 4; } diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java index 0a539f19c48a..8016a6559bcc 100644 --- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java +++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java @@ -111,14 +111,7 @@ public class UnlaunchableAppActivity extends Activity @Override public void onClick(DialogInterface dialog, int which) { if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) { - if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget) - && mTarget != null) { - try { - startIntentSenderForResult(mTarget, -1, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - /* ignore */ - } - } + UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget); } } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index fbdf17d84aa8..6fb02b162309 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -28,6 +28,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -137,7 +138,7 @@ public final class ProcessState { private final String mName; private final String mPackage; private final int mUid; - private final int mVersion; + private final long mVersion; private final DurationsTable mDurations; private final PssTable mPssTable; @@ -170,7 +171,7 @@ public final class ProcessState { * Create a new top-level process state, for the initial case where there is only * a single package running in a process. The initial state is not running. */ - public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) { + public ProcessState(ProcessStats processStats, String pkg, int uid, long vers, String name) { mStats = processStats; mName = name; mCommonProcess = this; @@ -186,7 +187,7 @@ public final class ProcessState { * state. The current running state of the top-level process is also copied, * marked as started running at 'now'. */ - public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name, + public ProcessState(ProcessState commonProcess, String pkg, int uid, long vers, String name, long now) { mStats = commonProcess.mStats; mName = name; @@ -238,7 +239,7 @@ public final class ProcessState { return mUid; } - public int getVersion() { + public long getVersion() { return mVersion; } @@ -546,7 +547,7 @@ public final class ProcessState { // The array map is still pointing to a common process state // that is now shared across packages. Update it to point to // the new per-package state. - SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid); + LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid); if (vpkg == null) { throw new IllegalStateException("Didn't find package " + pkgName + " / " + mUid); @@ -584,7 +585,7 @@ public final class ProcessState { // The array map is still pointing to a common process state // that is now shared across packages. Update it to point to // the new per-package state. - SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index), + LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index), proc.mUid); if (vpkg == null) { throw new IllegalStateException("No existing package " @@ -1037,7 +1038,7 @@ public final class ProcessState { } } - public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers, + public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, long vers, String itemName, long now) { pw.print("pkgproc,"); pw.print(pkgName); diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 14f5e5b5a704..2ce7936d157b 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -28,6 +28,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -157,7 +158,7 @@ public final class ProcessStats implements Parcelable { }; // Current version of the parcel format. - private static final int PARCEL_VERSION = 21; + private static final int PARCEL_VERSION = 22; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; @@ -165,9 +166,8 @@ public final class ProcessStats implements Parcelable { public String mTimePeriodStartClockStr; public int mFlags; - public final ProcessMap<SparseArray<PackageState>> mPackages - = new ProcessMap<SparseArray<PackageState>>(); - public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); + public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>(); + public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>(); public final long[] mMemFactorDurations = new long[ADJ_COUNT]; public int mMemFactor = STATE_NOTHING; @@ -218,15 +218,16 @@ public final class ProcessStats implements Parcelable { } public void add(ProcessStats other) { - ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap(); + ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + other.mPackages.getMap(); for (int ip=0; ip<pkgMap.size(); ip++) { final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { final int uid = uids.keyAt(iu); - final SparseArray<PackageState> versions = uids.valueAt(iu); + final LongSparseArray<PackageState> versions = uids.valueAt(iu); for (int iv=0; iv<versions.size(); iv++) { - final int vers = versions.keyAt(iv); + final long vers = versions.keyAt(iv); final PackageState otherState = versions.valueAt(iv); final int NPROCS = otherState.mProcesses.size(); final int NSRVS = otherState.mServices.size(); @@ -269,7 +270,7 @@ public final class ProcessStats implements Parcelable { ProcessState otherProc = uids.valueAt(iu); final String name = otherProc.getName(); final String pkg = otherProc.getPackage(); - final int vers = otherProc.getVersion(); + final long vers = otherProc.getVersion(); ProcessState thisProc = mProcesses.get(name, uid); if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + name); if (thisProc == null) { @@ -420,11 +421,12 @@ public final class ProcessStats implements Parcelable { // Next reset or prune all per-package processes, and for the ones that are reset // track this back to the common processes. - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); for (int ip=pkgMap.size()-1; ip>=0; ip--) { - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); for (int iu=uids.size()-1; iu>=0; iu--) { - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); for (int iv=vpkgs.size()-1; iv>=0; iv--) { final PackageState pkgState = vpkgs.valueAt(iv); for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { @@ -727,13 +729,14 @@ public final class ProcessStats implements Parcelable { uids.valueAt(iu).commitStateTime(now); } } - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); final int NPKG = pkgMap.size(); for (int ip=0; ip<NPKG; ip++) { - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); final int NUID = uids.size(); for (int iu=0; iu<NUID; iu++) { - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); final int NVERS = vpkgs.size(); for (int iv=0; iv<NVERS; iv++) { PackageState pkgState = vpkgs.valueAt(iv); @@ -781,23 +784,23 @@ public final class ProcessStats implements Parcelable { out.writeInt(uids.keyAt(iu)); final ProcessState proc = uids.valueAt(iu); writeCommonString(out, proc.getPackage()); - out.writeInt(proc.getVersion()); + out.writeLong(proc.getVersion()); proc.writeToParcel(out, now); } } out.writeInt(NPKG); for (int ip=0; ip<NPKG; ip++) { writeCommonString(out, pkgMap.keyAt(ip)); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); final int NUID = uids.size(); out.writeInt(NUID); for (int iu=0; iu<NUID; iu++) { out.writeInt(uids.keyAt(iu)); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); final int NVERS = vpkgs.size(); out.writeInt(NVERS); for (int iv=0; iv<NVERS; iv++) { - out.writeInt(vpkgs.keyAt(iv)); + out.writeLong(vpkgs.keyAt(iv)); final PackageState pkgState = vpkgs.valueAt(iv); final int NPROCS = pkgState.mProcesses.size(); out.writeInt(NPROCS); @@ -963,7 +966,7 @@ public final class ProcessStats implements Parcelable { mReadError = "bad process package name"; return; } - final int vers = in.readInt(); + final long vers = in.readLong(); ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; if (proc != null) { if (!proc.readFromParcel(in, false)) { @@ -1014,11 +1017,11 @@ public final class ProcessStats implements Parcelable { } while (NVERS > 0) { NVERS--; - final int vers = in.readInt(); + final long vers = in.readLong(); PackageState pkgState = new PackageState(pkgName, uid); - SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid); + LongSparseArray<PackageState> vpkg = mPackages.get(pkgName, uid); if (vpkg == null) { - vpkg = new SparseArray<PackageState>(); + vpkg = new LongSparseArray<>(); mPackages.put(pkgName, uid, vpkg); } vpkg.put(vers, pkgState); @@ -1117,10 +1120,10 @@ public final class ProcessStats implements Parcelable { if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!"); } - public PackageState getPackageStateLocked(String packageName, int uid, int vers) { - SparseArray<PackageState> vpkg = mPackages.get(packageName, uid); + public PackageState getPackageStateLocked(String packageName, int uid, long vers) { + LongSparseArray<PackageState> vpkg = mPackages.get(packageName, uid); if (vpkg == null) { - vpkg = new SparseArray<PackageState>(); + vpkg = new LongSparseArray<PackageState>(); mPackages.put(packageName, uid, vpkg); } PackageState as = vpkg.get(vers); @@ -1132,7 +1135,7 @@ public final class ProcessStats implements Parcelable { return as; } - public ProcessState getProcessStateLocked(String packageName, int uid, int vers, + public ProcessState getProcessStateLocked(String packageName, int uid, long vers, String processName) { final PackageState pkgState = getPackageStateLocked(packageName, uid, vers); ProcessState ps = pkgState.mProcesses.get(processName); @@ -1202,7 +1205,7 @@ public final class ProcessStats implements Parcelable { return ps; } - public ServiceState getServiceStateLocked(String packageName, int uid, int vers, + public ServiceState getServiceStateLocked(String packageName, int uid, long vers, String processName, String className) { final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers); ServiceState ss = as.mServices.get(className); @@ -1228,16 +1231,16 @@ public final class ProcessStats implements Parcelable { mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ); sepNeeded = true; } - ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap(); boolean printedHeader = false; for (int ip=0; ip<pkgMap.size(); ip++) { final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { final int uid = uids.keyAt(iu); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); for (int iv=0; iv<vpkgs.size(); iv++) { - final int vers = vpkgs.keyAt(iv); + final long vers = vpkgs.keyAt(iv); final PackageState pkgState = vpkgs.valueAt(iv); final int NPROCS = pkgState.mProcesses.size(); final int NSRVS = pkgState.mServices.size(); @@ -1531,12 +1534,13 @@ public final class ProcessStats implements Parcelable { int[] procStates, int sortProcStates[], long now, String reqPackage, boolean activeOnly) { final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); for (int ip=0; ip<pkgMap.size(); ip++) { final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> procs = pkgMap.valueAt(ip); for (int iu=0; iu<procs.size(); iu++) { - final SparseArray<PackageState> vpkgs = procs.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = procs.valueAt(iu); final int NVERS = vpkgs.size(); for (int iv=0; iv<NVERS; iv++) { final PackageState state = vpkgs.valueAt(iv); @@ -1571,7 +1575,8 @@ public final class ProcessStats implements Parcelable { public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { final long now = SystemClock.uptimeMillis(); - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); pw.println("vers,5"); pw.print("period,"); pw.print(mTimePeriodStartClockStr); pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); @@ -1602,12 +1607,12 @@ public final class ProcessStats implements Parcelable { if (reqPackage != null && !reqPackage.equals(pkgName)) { continue; } - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { final int uid = uids.keyAt(iu); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); for (int iv=0; iv<vpkgs.size(); iv++) { - final int vers = vpkgs.keyAt(iv); + final long vers = vpkgs.keyAt(iv); final PackageState pkgState = vpkgs.valueAt(iv); final int NPROCS = pkgState.mProcesses.size(); final int NSRVS = pkgState.mServices.size(); @@ -1709,7 +1714,8 @@ public final class ProcessStats implements Parcelable { } public void toProto(ProtoOutputStream proto, long now) { - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime); proto.write(ProcessStatsSectionProto.END_REALTIME_MS, @@ -1750,10 +1756,10 @@ public final class ProcessStats implements Parcelable { } final public static class ProcessStateHolder { - public final int appVersion; + public final long appVersion; public ProcessState state; - public ProcessStateHolder(int _appVersion) { + public ProcessStateHolder(long _appVersion) { appVersion = _appVersion; } } diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java index 2e11c43872f6..650de2ea2b68 100644 --- a/core/java/com/android/internal/app/procstats/ServiceState.java +++ b/core/java/com/android/internal/app/procstats/ServiceState.java @@ -441,7 +441,7 @@ public final class ServiceState { return totalTime; } - public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers, + public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers, String serviceName, long now) { dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName, ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now); @@ -454,7 +454,7 @@ public final class ServiceState { } private void dumpTimeCheckin(PrintWriter pw, String label, String packageName, - int uid, int vers, String serviceName, int serviceType, int opCount, + int uid, long vers, String serviceName, int serviceType, int opCount, int curState, long curStartTime, long now) { if (opCount <= 0) { return; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 3d49072ea8e8..a050a3ce0cf2 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -120,7 +120,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 169 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 170 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS; @@ -4501,11 +4501,12 @@ public class BatteryStatsImpl extends BatteryStats { } } - public void notePackageInstalledLocked(String pkgName, int versionCode) { + public void notePackageInstalledLocked(String pkgName, long versionCode) { final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); + // XXX need to figure out what to do with long version codes. addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED, - pkgName, versionCode); + pkgName, (int)versionCode); PackageChange pc = new PackageChange(); pc.mPackageName = pkgName; pc.mUpdate = true; @@ -9283,7 +9284,7 @@ public class BatteryStatsImpl extends BatteryStats { if (pc.mUpdate) { out.startTag(null, "upd"); out.attribute(null, "pkg", pc.mPackageName); - out.attribute(null, "ver", Integer.toString(pc.mVersionCode)); + out.attribute(null, "ver", Long.toString(pc.mVersionCode)); out.endTag(null, "upd"); } else { out.startTag(null, "rem"); @@ -9412,7 +9413,7 @@ public class BatteryStatsImpl extends BatteryStats { pc.mUpdate = true; pc.mPackageName = parser.getAttributeValue(null, "pkg"); String verStr = parser.getAttributeValue(null, "ver"); - pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0; + pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0; dit.mPackageChanges.add(pc); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("rem")) { @@ -12113,7 +12114,7 @@ public class BatteryStatsImpl extends BatteryStats { PackageChange pc = new PackageChange(); pc.mPackageName = in.readString(); pc.mUpdate = in.readInt() != 0; - pc.mVersionCode = in.readInt(); + pc.mVersionCode = in.readLong(); mDailyPackageChanges.add(pc); } } else { @@ -12538,7 +12539,7 @@ public class BatteryStatsImpl extends BatteryStats { PackageChange pc = mDailyPackageChanges.get(i); out.writeString(pc.mPackageName); out.writeInt(pc.mUpdate ? 1 : 0); - out.writeInt(pc.mVersionCode); + out.writeLong(pc.mVersionCode); } } else { out.writeInt(0); diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 22bfcc347c3a..9336ddd05c28 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -425,14 +425,17 @@ public class ArrayUtils { * Adds value to given array if not already present, providing set-like * behavior. */ - public static @NonNull long[] appendLong(@Nullable long[] cur, long val) { + public static @NonNull long[] appendLong(@Nullable long[] cur, long val, + boolean allowDuplicates) { if (cur == null) { return new long[] { val }; } final int N = cur.length; - for (int i = 0; i < N; i++) { - if (cur[i] == val) { - return cur; + if (!allowDuplicates) { + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + return cur; + } } } long[] ret = new long[N + 1]; @@ -442,6 +445,14 @@ public class ArrayUtils { } /** + * Adds value to given array if not already present, providing set-like + * behavior. + */ + public static @NonNull long[] appendLong(@Nullable long[] cur, long val) { + return appendLong(cur, val, false); + } + + /** * Removes value from given array if present, providing set-like behavior. */ public static @Nullable long[] removeLong(@Nullable long[] cur, long val) { diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index f0b47de8be98..f983de17e0b1 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -30,7 +30,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.function.*; +import java.util.function.Function; import java.util.stream.Stream; /** diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java index cdef97e84f62..eb92c1c0dfb2 100644 --- a/core/java/com/android/internal/util/FunctionalUtils.java +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -32,7 +32,7 @@ public class FunctionalUtils { */ @FunctionalInterface public interface ThrowingRunnable { - void run() throws Exception; + void runOrThrow() throws Exception; } /** @@ -43,7 +43,7 @@ public class FunctionalUtils { */ @FunctionalInterface public interface ThrowingSupplier<T> { - T get() throws Exception; + T getOrThrow() throws Exception; } /** diff --git a/core/java/com/android/internal/util/function/QuadConsumer.java b/core/java/com/android/internal/util/function/QuadConsumer.java new file mode 100644 index 000000000000..d899c01b16c6 --- /dev/null +++ b/core/java/com/android/internal/util/function/QuadConsumer.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Consumer; + +/** + * A 4-argument {@link Consumer} + * + * @hide + */ +public interface QuadConsumer<A, B, C, D> { + void accept(A a, B b, C c, D d); +} diff --git a/core/java/com/android/internal/util/function/QuadFunction.java b/core/java/com/android/internal/util/function/QuadFunction.java new file mode 100644 index 000000000000..700d9536409d --- /dev/null +++ b/core/java/com/android/internal/util/function/QuadFunction.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Function; + +/** + * A 4-argument {@link Function} + * + * @hide + */ +public interface QuadFunction<A, B, C, D, R> { + R apply(A a, B b, C c, D d); +} diff --git a/core/java/com/android/internal/util/function/QuadPredicate.java b/core/java/com/android/internal/util/function/QuadPredicate.java new file mode 100644 index 000000000000..512c98ba1e47 --- /dev/null +++ b/core/java/com/android/internal/util/function/QuadPredicate.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Predicate; + +/** + * A 4-argument {@link Predicate} + * + * @hide + */ +public interface QuadPredicate<A, B, C, D> { + boolean test(A a, B b, C c, D d); +} diff --git a/core/java/com/android/internal/util/function/TriConsumer.java b/core/java/com/android/internal/util/function/TriConsumer.java new file mode 100644 index 000000000000..40d614ec5aff --- /dev/null +++ b/core/java/com/android/internal/util/function/TriConsumer.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Consumer; + +/** + * A 3-argument {@link Consumer} + * + * @hide + */ +public interface TriConsumer<A, B, C> { + void accept(A a, B b, C c); +} diff --git a/core/java/com/android/internal/util/function/TriFunction.java b/core/java/com/android/internal/util/function/TriFunction.java new file mode 100644 index 000000000000..2b1df86e72e2 --- /dev/null +++ b/core/java/com/android/internal/util/function/TriFunction.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Function; + +/** + * A 3-argument {@link Function} + * + * @hide + */ +public interface TriFunction<A, B, C, R> { + R apply(A a, B b, C c); +} diff --git a/core/java/com/android/internal/util/function/TriPredicate.java b/core/java/com/android/internal/util/function/TriPredicate.java new file mode 100644 index 000000000000..d9cd9683ee26 --- /dev/null +++ b/core/java/com/android/internal/util/function/TriPredicate.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.function; + + +import java.util.function.Predicate; + +/** + * A 3-argument {@link Predicate} + * + * @hide + */ +public interface TriPredicate<A, B, C> { + boolean test(A a, B b, C c); +} diff --git a/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java b/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java new file mode 100644 index 000000000000..cf86b7171864 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java @@ -0,0 +1,33 @@ +/* + * 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.internal.util.function.pooled; + +/** + * A placeholder for an argument of type {@code R} + * + * @see PooledLambda + * @hide + */ +public final class ArgumentPlaceholder<R> { + private ArgumentPlaceholder() {} + static final ArgumentPlaceholder<?> INSTANCE = new ArgumentPlaceholder<>(); + + @Override + public String toString() { + return "_"; + } +} diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java new file mode 100755 index 000000000000..c0f506ec889f --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java @@ -0,0 +1,132 @@ +/* + * 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.internal.util.function.pooled; + +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; +import com.android.internal.util.function.QuadConsumer; +import com.android.internal.util.function.QuadFunction; +import com.android.internal.util.function.TriConsumer; +import com.android.internal.util.function.TriFunction; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; + +/** + * An interface implementing all supported function interfaces, delegating each to {@link #invoke} + * + * @hide + */ +abstract class OmniFunction<A, B, C, D, R> implements + PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>, + QuadFunction<A, B, C, D, R>, + PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, + PooledPredicate<A>, BiPredicate<A, B>, + PooledSupplier<R>, PooledRunnable, + ThrowingRunnable, ThrowingSupplier<R>, + PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble { + + abstract R invoke(A a, B b, C c, D d); + + @Override + public R apply(A o, B o2) { + return invoke(o, o2, null, null); + } + + @Override + public R apply(A o) { + return invoke(o, null, null, null); + } + + abstract public <V> OmniFunction<A, B, C, D, V> andThen(Function<? super R, ? extends V> after); + abstract public OmniFunction<A, B, C, D, R> negate(); + + @Override + public void accept(A o, B o2) { + invoke(o, o2, null, null); + } + + @Override + public void accept(A o) { + invoke(o, null, null, null); + } + + @Override + public void run() { + invoke(null, null, null, null); + } + + @Override + public R get() { + return invoke(null, null, null, null); + } + + @Override + public boolean test(A o, B o2) { + return (Boolean) invoke(o, o2, null, null); + } + + @Override + public boolean test(A o) { + return (Boolean) invoke(o, null, null, null); + } + + @Override + public PooledRunnable asRunnable() { + return this; + } + + @Override + public PooledConsumer<A> asConsumer() { + return this; + } + + @Override + public R apply(A a, B b, C c) { + return invoke(a, b, c, null); + } + + @Override + public void accept(A a, B b, C c) { + invoke(a, b, c, null); + } + + @Override + public R apply(A a, B b, C c, D d) { + return invoke(a, b, c, d); + } + + @Override + public void accept(A a, B b, C c, D d) { + invoke(a, b, c, d); + } + + @Override + public void runOrThrow() throws Exception { + run(); + } + + @Override + public R getOrThrow() throws Exception { + return get(); + } + + @Override + abstract public OmniFunction<A, B, C, D, R> recycleOnUse(); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledConsumer.java b/core/java/com/android/internal/util/function/pooled/PooledConsumer.java new file mode 100644 index 000000000000..f66586ee6791 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledConsumer.java @@ -0,0 +1,31 @@ +/* + * 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.internal.util.function.pooled; + +import java.util.function.Consumer; + +/** + * {@link Consumer} + {@link PooledLambda} + * + * @see PooledLambda + * @hide + */ +public interface PooledConsumer<T> extends PooledLambda, Consumer<T> { + + /** @inheritDoc */ + PooledConsumer<T> recycleOnUse(); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledFunction.java b/core/java/com/android/internal/util/function/pooled/PooledFunction.java new file mode 100644 index 000000000000..1f166fafc7e6 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledFunction.java @@ -0,0 +1,36 @@ +/* + * 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.internal.util.function.pooled; + +import java.util.function.Function; + +/** + * {@link Function} + {@link PooledLambda} + * + * @see PooledLambda + * @hide + */ +public interface PooledFunction<A, R> extends PooledLambda, Function<A, R> { + + /** + * Ignores the result + */ + PooledConsumer<A> asConsumer(); + + /** @inheritDoc */ + PooledFunction<A, R> recycleOnUse(); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java new file mode 100755 index 000000000000..17b140dec396 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -0,0 +1,813 @@ +/* + * 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.internal.util.function.pooled; + +import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire; +import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquireConstSupplier; + +import android.os.Message; + +import com.android.internal.util.function.QuadConsumer; +import com.android.internal.util.function.QuadFunction; +import com.android.internal.util.function.TriConsumer; +import com.android.internal.util.function.TriFunction; +import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * A recyclable anonymous function. + * Allows obtaining {@link Function}s/{@link Runnable}s/{@link Supplier}s/etc. without allocating a + * new instance each time + * + * This exploits the mechanic that stateless lambdas (such as plain/non-bound method references) + * get translated into a singleton instance, making it possible to create a recyclable container + * ({@link PooledLambdaImpl}) holding a reference to such a singleton function, as well as + * (possibly partial) arguments required for its invocation. + * + * To obtain an instance, use one of the factory methods in this class. + * + * You can call {@link #recycleOnUse} to make the instance automatically recycled upon invocation, + * making if effectively <b>one-time use</b>. + * This is often the behavior you want, as it allows to not worry about manual recycling. + * Some notable examples: {@link android.os.Handler#post(Runnable)}, + * {@link android.app.Activity#runOnUiThread(Runnable)}, {@link android.view.View#post(Runnable)} + * + * For factories of functions that take further arguments, the corresponding 'missing' argument's + * position is marked by an argument of type {@link ArgumentPlaceholder} with the type parameter + * corresponding to missing argument's type. + * You can fill the 'missing argument' spot with {@link #__()} + * (which is the factory function for {@link ArgumentPlaceholder}) + * + * @hide + */ +@SuppressWarnings({"unchecked", "unused", "WeakerAccess"}) +public interface PooledLambda { + + /** + * Recycles this instance. No-op if already recycled. + */ + void recycle(); + + /** + * Makes this instance automatically {@link #recycle} itself after the first call. + * + * @return this instance for convenience + */ + PooledLambda recycleOnUse(); + + + // Factories + + /** + * @return {@link ArgumentPlaceholder} with the inferred type parameter value + */ + static <R> ArgumentPlaceholder<R> __() { + return (ArgumentPlaceholder<R>) ArgumentPlaceholder.INSTANCE; + } + + /** + * @param typeHint the explicitly specified type of the missing argument + * @return {@link ArgumentPlaceholder} with the specified type parameter value + */ + static <R> ArgumentPlaceholder<R> __(Class<R> typeHint) { + return __(); + } + + /** + * Wraps the given value into a {@link PooledSupplier} + * + * @param value a value to wrap + * @return a pooled supplier of {@code value} + */ + static <R> PooledSupplier<R> obtainSupplier(R value) { + PooledLambdaImpl r = acquireConstSupplier(ReturnType.OBJECT); + r.mFunc = value; + return r; + } + + /** + * Wraps the given value into a {@link PooledSupplier} + * + * @param value a value to wrap + * @return a pooled supplier of {@code value} + */ + static PooledSupplier.OfInt obtainSupplier(int value) { + PooledLambdaImpl r = acquireConstSupplier(ReturnType.INT); + r.mConstValue = value; + return r; + } + + /** + * Wraps the given value into a {@link PooledSupplier} + * + * @param value a value to wrap + * @return a pooled supplier of {@code value} + */ + static PooledSupplier.OfLong obtainSupplier(long value) { + PooledLambdaImpl r = acquireConstSupplier(ReturnType.LONG); + r.mConstValue = value; + return r; + } + + /** + * Wraps the given value into a {@link PooledSupplier} + * + * @param value a value to wrap + * @return a pooled supplier of {@code value} + */ + static PooledSupplier.OfDouble obtainSupplier(double value) { + PooledLambdaImpl r = acquireConstSupplier(ReturnType.DOUBLE); + r.mConstValue = Double.doubleToRawLongBits(value); + return r; + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1) } + */ + static <A> PooledRunnable obtainRunnable( + Consumer<? super A> function, + A arg1) { + return acquire(PooledLambdaImpl.sPool, + function, 1, 0, ReturnType.VOID, arg1, null, null, null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1) } + */ + static <A> PooledSupplier<Boolean> obtainSupplier( + Predicate<? super A> function, + A arg1) { + return acquire(PooledLambdaImpl.sPool, + function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1) } + */ + static <A, R> PooledSupplier<R> obtainSupplier( + Function<? super A, ? extends R> function, + A arg1) { + return acquire(PooledLambdaImpl.sPool, + function, 1, 0, ReturnType.OBJECT, arg1, null, null, null); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(Consumer, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1) } when handled + */ + static <A> Message obtainMessage( + Consumer<? super A> function, + A arg1) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 1, 0, ReturnType.VOID, arg1, null, null, null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2) } + */ + static <A, B> PooledRunnable obtainRunnable( + BiConsumer<? super A, ? super B> function, + A arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2) } + */ + static <A, B> PooledSupplier<Boolean> obtainSupplier( + BiPredicate<? super A, ? super B> function, + A arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2) } + */ + static <A, B, R> PooledSupplier<R> obtainSupplier( + BiFunction<? super A, ? super B, ? extends R> function, + A arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2) } + */ + static <A, B> PooledConsumer<A> obtainConsumer( + BiConsumer<? super A, ? super B> function, + ArgumentPlaceholder<A> arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null); + } + + /** + * {@link PooledPredicate} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledPredicate}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2) } + */ + static <A, B> PooledPredicate<A> obtainPredicate( + BiPredicate<? super A, ? super B> function, + ArgumentPlaceholder<A> arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2) } + */ + static <A, B, R> PooledFunction<A, R> obtainFunction( + BiFunction<? super A, ? super B, ? extends R> function, + ArgumentPlaceholder<A> arg1, B arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2) } + */ + static <A, B> PooledConsumer<B> obtainConsumer( + BiConsumer<? super A, ? super B> function, + A arg1, ArgumentPlaceholder<B> arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null); + } + + /** + * {@link PooledPredicate} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledPredicate}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2) } + */ + static <A, B> PooledPredicate<B> obtainPredicate( + BiPredicate<? super A, ? super B> function, + A arg1, ArgumentPlaceholder<B> arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2) } + */ + static <A, B, R> PooledFunction<B, R> obtainFunction( + BiFunction<? super A, ? super B, ? extends R> function, + A arg1, ArgumentPlaceholder<B> arg2) { + return acquire(PooledLambdaImpl.sPool, + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(BiConsumer, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2) } when handled + */ + static <A, B> Message obtainMessage( + BiConsumer<? super A, ? super B> function, + A arg1, B arg2) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3) } + */ + static <A, B, C> PooledRunnable obtainRunnable( + TriConsumer<? super A, ? super B, ? super C> function, + A arg1, B arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3) } + */ + static <A, B, C, R> PooledSupplier<R> obtainSupplier( + TriFunction<? super A, ? super B, ? super C, ? extends R> function, + A arg1, B arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2, arg3) } + */ + static <A, B, C> PooledConsumer<A> obtainConsumer( + TriConsumer<? super A, ? super B, ? super C> function, + ArgumentPlaceholder<A> arg1, B arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2, arg3) } + */ + static <A, B, C, R> PooledFunction<A, R> obtainFunction( + TriFunction<? super A, ? super B, ? super C, ? extends R> function, + ArgumentPlaceholder<A> arg1, B arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2, arg3) } + */ + static <A, B, C> PooledConsumer<B> obtainConsumer( + TriConsumer<? super A, ? super B, ? super C> function, + A arg1, ArgumentPlaceholder<B> arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2, arg3) } + */ + static <A, B, C, R> PooledFunction<B, R> obtainFunction( + TriFunction<? super A, ? super B, ? super C, ? extends R> function, + A arg1, ArgumentPlaceholder<B> arg2, C arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg3) -> function(arg1, arg2, arg3) } + */ + static <A, B, C> PooledConsumer<C> obtainConsumer( + TriConsumer<? super A, ? super B, ? super C> function, + A arg1, B arg2, ArgumentPlaceholder<C> arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg3) -> function(arg1, arg2, arg3) } + */ + static <A, B, C, R> PooledFunction<C, R> obtainFunction( + TriFunction<? super A, ? super B, ? super C, ? extends R> function, + A arg1, B arg2, ArgumentPlaceholder<C> arg3) { + return acquire(PooledLambdaImpl.sPool, + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(TriConsumer, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3) } when handled + */ + static <A, B, C> Message obtainMessage( + TriConsumer<? super A, ? super B, ? super C> function, + A arg1, B arg2, C arg3) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D> PooledRunnable obtainRunnable( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + A arg1, B arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D, R> PooledSupplier<R> obtainSupplier( + QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, + A arg1, B arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D> PooledConsumer<A> obtainConsumer( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 placeholder for a missing argument. Use {@link #__} to get one + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg1) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D, R> PooledFunction<A, R> obtainFunction( + QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, + ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D> PooledConsumer<B> obtainConsumer( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 placeholder for a missing argument. Use {@link #__} to get one + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg2) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D, R> PooledFunction<B, R> obtainFunction( + QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, + A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 placeholder for a missing argument. Use {@link #__} to get one + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg3) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D> PooledConsumer<C> obtainConsumer( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 placeholder for a missing argument. Use {@link #__} to get one + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg3) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D, R> PooledFunction<C, R> obtainFunction( + QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, + A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledConsumer} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledConsumer}, equivalent to lambda: + * {@code (arg4) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D> PooledConsumer<D> obtainConsumer( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4); + } + + /** + * {@link PooledFunction} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 placeholder for a missing argument. Use {@link #__} to get one + * @return a {@link PooledFunction}, equivalent to lambda: + * {@code (arg4) -> function(arg1, arg2, arg3, arg4) } + */ + static <A, B, C, D, R> PooledFunction<D, R> obtainFunction( + QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, + A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { + return acquire(PooledLambdaImpl.sPool, + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuadConsumer, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4) } when handled + */ + static <A, B, C, D> Message obtainMessage( + QuadConsumer<? super A, ? super B, ? super C, ? super D> function, + A arg1, B arg2, C arg3, D arg4) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java new file mode 100755 index 000000000000..03e013cd46b8 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -0,0 +1,557 @@ +/* + * 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.internal.util.function.pooled; + +import android.annotation.Nullable; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pools; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.BitUtils; +import com.android.internal.util.function.QuadConsumer; +import com.android.internal.util.function.QuadFunction; +import com.android.internal.util.function.QuadPredicate; +import com.android.internal.util.function.TriConsumer; +import com.android.internal.util.function.TriFunction; +import com.android.internal.util.function.TriPredicate; + +import java.util.Arrays; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * @see PooledLambda + * @hide + */ +final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Object, R> { + + private static final boolean DEBUG = false; + private static final String LOG_TAG = "PooledLambdaImpl"; + + private static final int MAX_ARGS = 4; + + private static final int MAX_POOL_SIZE = 50; + + static class Pool extends Pools.SynchronizedPool<PooledLambdaImpl> { + + public Pool(Object lock) { + super(MAX_POOL_SIZE, lock); + } + } + + static final Pool sPool = new Pool(new Object()); + static final Pool sMessageCallbacksPool = new Pool(Message.sPoolSync); + + private PooledLambdaImpl() {} + + /** + * The function reference to be invoked + * + * May be the return value itself in case when an immediate result constant is provided instead + */ + Object mFunc; + + /** + * A primitive result value to be immediately returned on invocation instead of calling + * {@link #mFunc} + */ + long mConstValue; + + /** + * Arguments for {@link #mFunc} + */ + @Nullable Object[] mArgs = null; + + /** + * Flag for {@link #mFlags} + * + * Indicates whether this instance is recycled + */ + private static final int FLAG_RECYCLED = 1 << MAX_ARGS; + + /** + * Flag for {@link #mFlags} + * + * Indicates whether this instance should be immediately recycled on invocation + * (as requested via {@link PooledLambda#recycleOnUse()}) or not(default) + */ + private static final int FLAG_RECYCLE_ON_USE = 1 << (MAX_ARGS + 1); + + /** + * Flag for {@link #mFlags} + * + * Indicates that this instance was acquired from {@link #sMessageCallbacksPool} as opposed to + * {@link #sPool} + */ + private static final int FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL = 1 << (MAX_ARGS + 2); + + /** @see #mFlags */ + static final int MASK_EXPOSED_AS = LambdaType.MASK << (MAX_ARGS + 3); + + /** @see #mFlags */ + static final int MASK_FUNC_TYPE = LambdaType.MASK << + (MAX_ARGS + 3 + LambdaType.MASK_BIT_COUNT); + + /** + * Bit schema: + * AAAABCDEEEEEEFFFFFF + * + * Where: + * A - whether {@link #mArgs arg} at corresponding index was specified at + * {@link #acquire creation time} (0) or {@link #invoke invocation time} (1) + * B - {@link #FLAG_RECYCLED} + * C - {@link #FLAG_RECYCLE_ON_USE} + * D - {@link #FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL} + * E - {@link LambdaType} representing the type of the lambda returned to the caller from a + * factory method + * F - {@link LambdaType} of {@link #mFunc} as resolved when calling a factory method + */ + int mFlags = 0; + + + @Override + public void recycle() { + if (DEBUG) Log.i(LOG_TAG, this + ".recycle()"); + if (!isRecycled()) doRecycle(); + } + + private void doRecycle() { + if (DEBUG) Log.i(LOG_TAG, this + ".doRecycle()"); + Pool pool = (mFlags & FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL) != 0 + ? PooledLambdaImpl.sMessageCallbacksPool + : PooledLambdaImpl.sPool; + + mFunc = null; + if (mArgs != null) Arrays.fill(mArgs, null); + mFlags = FLAG_RECYCLED; + mConstValue = 0L; + + pool.release(this); + } + + @Override + R invoke(Object a1, Object a2, Object a3, Object a4) { + checkNotRecycled(); + if (DEBUG) { + Log.i(LOG_TAG, this + ".invoke(" + + commaSeparateFirstN( + new Object[] { a1, a2, a3, a4 }, + LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS))) + + ")"); + } + boolean ignored = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4); + int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)); + if (argCount != LambdaType.MASK_ARG_COUNT) { + for (int i = 0; i < argCount; i++) { + if (mArgs[i] == ArgumentPlaceholder.INSTANCE) { + throw new IllegalStateException("Missing argument #" + i + " among " + + Arrays.toString(mArgs)); + } + } + } + try { + return doInvoke(); + } finally { + if (isRecycleOnUse()) doRecycle(); + if (!isRecycled()) { + int argsSize = ArrayUtils.size(mArgs); + for (int i = 0; i < argsSize; i++) { + popArg(i); + } + } + } + } + + private boolean fillInArg(Object invocationArg) { + int argsSize = ArrayUtils.size(mArgs); + for (int i = 0; i < argsSize; i++) { + if (mArgs[i] == ArgumentPlaceholder.INSTANCE) { + mArgs[i] = invocationArg; + mFlags |= BitUtils.bitAt(i); + return true; + } + } + if (invocationArg != null && invocationArg != ArgumentPlaceholder.INSTANCE) { + throw new IllegalStateException("No more arguments expected for provided arg " + + invocationArg + " among " + Arrays.toString(mArgs)); + } + return false; + } + + private void checkNotRecycled() { + if (isRecycled()) throw new IllegalStateException("Instance is recycled: " + this); + } + + @SuppressWarnings("unchecked") + private R doInvoke() { + final int funcType = getFlags(MASK_FUNC_TYPE); + final int argCount = LambdaType.decodeArgCount(funcType); + final int returnType = LambdaType.decodeReturnType(funcType); + + switch (argCount) { + case LambdaType.MASK_ARG_COUNT: { + switch (returnType) { + case LambdaType.ReturnType.INT: return (R) (Integer) getAsInt(); + case LambdaType.ReturnType.LONG: return (R) (Long) getAsLong(); + case LambdaType.ReturnType.DOUBLE: return (R) (Double) getAsDouble(); + default: return (R) mFunc; + } + } + case 0: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((Runnable) mFunc).run(); + return null; + } + case LambdaType.ReturnType.BOOLEAN: + case LambdaType.ReturnType.OBJECT: { + return (R) ((Supplier) mFunc).get(); + } + } + } break; + case 1: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((Consumer) mFunc).accept(popArg(0)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((Predicate) mFunc).test(popArg(0)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((Function) mFunc).apply(popArg(0)); + } + } + } break; + case 2: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((BiConsumer) mFunc).accept(popArg(0), popArg(1)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((BiPredicate) mFunc).test(popArg(0), popArg(1)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((BiFunction) mFunc).apply(popArg(0), popArg(1)); + } + } + } break; + case 3: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((TriConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((TriPredicate) mFunc).test( + popArg(0), popArg(1), popArg(2)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((TriFunction) mFunc).apply(popArg(0), popArg(1), popArg(2)); + } + } + } break; + case 4: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((QuadConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2), popArg(3)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((QuadPredicate) mFunc).test( + popArg(0), popArg(1), popArg(2), popArg(3)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((QuadFunction) mFunc).apply( + popArg(0), popArg(1), popArg(2), popArg(3)); + } + } + } break; + } + throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); + } + + private boolean isConstSupplier() { + return LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)) == LambdaType.MASK_ARG_COUNT; + } + + private Object popArg(int index) { + Object result = mArgs[index]; + if (isInvocationArgAtIndex(index)) { + mArgs[index] = ArgumentPlaceholder.INSTANCE; + mFlags &= ~BitUtils.bitAt(index); + } + return result; + } + + @Override + public String toString() { + if (isRecycled()) return "<recycled PooledLambda@" + hashCodeHex(this) + ">"; + + StringBuilder sb = new StringBuilder(); + if (isConstSupplier()) { + sb.append(getFuncTypeAsString()).append("(").append(doInvoke()).append(")"); + } else { + if (mFunc instanceof PooledLambdaImpl) { + sb.append(mFunc); + } else { + sb.append(getFuncTypeAsString()).append("@").append(hashCodeHex(mFunc)); + } + sb.append("("); + sb.append(commaSeparateFirstN(mArgs, LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)))); + sb.append(")"); + } + return sb.toString(); + } + + private String commaSeparateFirstN(@Nullable Object[] arr, int n) { + if (arr == null) return ""; + return TextUtils.join(",", Arrays.copyOf(arr, n)); + } + + private static String hashCodeHex(Object o) { + return Integer.toHexString(o.hashCode()); + } + + private String getFuncTypeAsString() { + if (isRecycled()) throw new IllegalStateException(); + if (isConstSupplier()) return "supplier"; + String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS)); + if (name.endsWith("Consumer")) return "consumer"; + if (name.endsWith("Function")) return "function"; + if (name.endsWith("Predicate")) return "predicate"; + if (name.endsWith("Supplier")) return "supplier"; + if (name.endsWith("Runnable")) return "runnable"; + throw new IllegalStateException("Don't know the string representation of " + name); + } + + /** + * Internal non-typesafe factory method for {@link PooledLambdaImpl} + */ + static <E extends PooledLambda> E acquire(Pool pool, Object f, + int fNumArgs, int numPlaceholders, int fReturnType, + Object a, Object b, Object c, Object d) { + PooledLambdaImpl r = acquire(pool); + if (DEBUG) { + Log.i(LOG_TAG, + "acquire(this = @" + hashCodeHex(r) + + ", f = " + f + + ", fNumArgs = " + fNumArgs + + ", numPlaceholders = " + numPlaceholders + + ", fReturnType = " + LambdaType.ReturnType.toString(fReturnType) + + ", a = " + a + + ", b = " + b + + ", c = " + c + + ", d = " + d + + ")"); + } + r.mFunc = f; + r.setFlags(MASK_FUNC_TYPE, LambdaType.encode(fNumArgs, fReturnType)); + r.setFlags(MASK_EXPOSED_AS, LambdaType.encode(numPlaceholders, fReturnType)); + if (ArrayUtils.size(r.mArgs) < fNumArgs) r.mArgs = new Object[fNumArgs]; + setIfInBounds(r.mArgs, 0, a); + setIfInBounds(r.mArgs, 1, b); + setIfInBounds(r.mArgs, 2, c); + setIfInBounds(r.mArgs, 3, d); + return (E) r; + } + + static PooledLambdaImpl acquireConstSupplier(int type) { + PooledLambdaImpl r = acquire(PooledLambdaImpl.sPool); + int lambdaType = LambdaType.encode(LambdaType.MASK_ARG_COUNT, type); + r.setFlags(PooledLambdaImpl.MASK_FUNC_TYPE, lambdaType); + r.setFlags(PooledLambdaImpl.MASK_EXPOSED_AS, lambdaType); + return r; + } + + static PooledLambdaImpl acquire(Pool pool) { + PooledLambdaImpl r = pool.acquire(); + if (r == null) r = new PooledLambdaImpl(); + r.mFlags &= ~FLAG_RECYCLED; + r.setFlags(FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL, + pool == sMessageCallbacksPool ? 1 : 0); + return r; + } + + private static void setIfInBounds(Object[] array, int i, Object a) { + if (i < ArrayUtils.size(array)) array[i] = a; + } + + @Override + public OmniFunction<Object, Object, Object, Object, R> negate() { + throw new UnsupportedOperationException(); + } + + @Override + public <V> OmniFunction<Object, Object, Object, Object, V> andThen( + Function<? super R, ? extends V> after) { + throw new UnsupportedOperationException(); + } + + @Override + public double getAsDouble() { + return Double.longBitsToDouble(mConstValue); + } + + @Override + public int getAsInt() { + return (int) mConstValue; + } + + @Override + public long getAsLong() { + return mConstValue; + } + + @Override + public OmniFunction<Object, Object, Object, Object, R> recycleOnUse() { + if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()"); + mFlags |= FLAG_RECYCLE_ON_USE; + return this; + } + + private boolean isRecycled() { + return (mFlags & FLAG_RECYCLED) != 0; + } + + private boolean isRecycleOnUse() { + return (mFlags & FLAG_RECYCLE_ON_USE) != 0; + } + + private boolean isInvocationArgAtIndex(int argIndex) { + return (mFlags & (1 << argIndex)) != 0; + } + + int getFlags(int mask) { + return unmask(mask, mFlags); + } + + void setFlags(int mask, int value) { + mFlags &= ~mask; + mFlags |= mask(mask, value); + } + + /** + * 0xFF000, 0xAB -> 0xAB000 + */ + private static int mask(int mask, int value) { + return (value << Integer.numberOfTrailingZeros(mask)) & mask; + } + + /** + * 0xFF000, 0xAB123 -> 0xAB + */ + private static int unmask(int mask, int bits) { + return (bits & mask) / (1 << Integer.numberOfTrailingZeros(mask)); + } + + /** + * Contract for encoding a supported lambda type in {@link #MASK_BIT_COUNT} bits + */ + static class LambdaType { + public static final int MASK_ARG_COUNT = 0b111; + public static final int MASK_RETURN_TYPE = 0b111000; + public static final int MASK = MASK_ARG_COUNT | MASK_RETURN_TYPE; + public static final int MASK_BIT_COUNT = 6; + + static int encode(int argCount, int returnType) { + return mask(MASK_ARG_COUNT, argCount) | mask(MASK_RETURN_TYPE, returnType); + } + + static int decodeArgCount(int type) { + return type & MASK_ARG_COUNT; + } + + static int decodeReturnType(int type) { + return unmask(MASK_RETURN_TYPE, type); + } + + static String toString(int type) { + int argCount = decodeArgCount(type); + int returnType = decodeReturnType(type); + if (argCount == 0) { + if (returnType == ReturnType.VOID) return "Runnable"; + if (returnType == ReturnType.OBJECT || returnType == ReturnType.BOOLEAN) { + return "Supplier"; + } + } + return argCountPrefix(argCount) + ReturnType.lambdaSuffix(returnType); + } + + private static String argCountPrefix(int argCount) { + switch (argCount) { + case MASK_ARG_COUNT: return ""; + case 1: return ""; + case 2: return "Bi"; + case 3: return "Tri"; + case 4: return "Quad"; + default: throw new IllegalArgumentException("" + argCount); + } + } + + static class ReturnType { + public static final int VOID = 1; + public static final int BOOLEAN = 2; + public static final int OBJECT = 3; + public static final int INT = 4; + public static final int LONG = 5; + public static final int DOUBLE = 6; + + static String toString(int returnType) { + switch (returnType) { + case VOID: return "VOID"; + case BOOLEAN: return "BOOLEAN"; + case OBJECT: return "OBJECT"; + case INT: return "INT"; + case LONG: return "LONG"; + case DOUBLE: return "DOUBLE"; + default: return "" + returnType; + } + } + + static String lambdaSuffix(int type) { + return prefix(type) + suffix(type); + } + + private static String prefix(int type) { + switch (type) { + case INT: return "Int"; + case LONG: return "Long"; + case DOUBLE: return "Double"; + default: return ""; + } + } + + private static String suffix(int type) { + switch (type) { + case VOID: return "Consumer"; + case BOOLEAN: return "Predicate"; + case OBJECT: return "Function"; + default: return "Supplier"; + } + } + } + } +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledPredicate.java b/core/java/com/android/internal/util/function/pooled/PooledPredicate.java new file mode 100644 index 000000000000..9b14366452e5 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledPredicate.java @@ -0,0 +1,36 @@ +/* + * 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.internal.util.function.pooled; + +import java.util.function.Predicate; + +/** + * {@link Predicate} + {@link PooledLambda} + * + * @see PooledLambda + * @hide + */ +public interface PooledPredicate<T> extends PooledLambda, Predicate<T> { + + /** + * Ignores the result + */ + PooledConsumer<T> asConsumer(); + + /** @inheritDoc */ + PooledPredicate<T> recycleOnUse(); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java new file mode 100644 index 000000000000..89ca82e2f3ce --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java @@ -0,0 +1,30 @@ +/* + * 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.internal.util.function.pooled; + +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; + +/** + * {@link Runnable} + {@link PooledLambda} + * + * @see PooledLambda + * @hide + */ +public interface PooledRunnable extends PooledLambda, Runnable, ThrowingRunnable { + /** @inheritDoc */ + PooledRunnable recycleOnUse(); +} diff --git a/core/java/com/android/internal/util/function/pooled/PooledSupplier.java b/core/java/com/android/internal/util/function/pooled/PooledSupplier.java new file mode 100644 index 000000000000..dd7f73eeff14 --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/PooledSupplier.java @@ -0,0 +1,59 @@ +/* + * 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.internal.util.function.pooled; + +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; + +import java.util.function.DoubleSupplier; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +/** + * {@link Supplier} + {@link PooledLambda} + * + * @see PooledLambda + * @hide + */ +public interface PooledSupplier<T> extends PooledLambda, Supplier<T>, ThrowingSupplier<T> { + + /** + * Ignores the result + */ + PooledRunnable asRunnable(); + + /** @inheritDoc */ + PooledSupplier<T> recycleOnUse(); + + /** {@link PooledLambda} + {@link IntSupplier} */ + interface OfInt extends IntSupplier, PooledLambda { + /** @inheritDoc */ + PooledSupplier.OfInt recycleOnUse(); + } + + /** {@link PooledLambda} + {@link LongSupplier} */ + interface OfLong extends LongSupplier, PooledLambda { + /** @inheritDoc */ + PooledSupplier.OfLong recycleOnUse(); + } + + /** {@link PooledLambda} + {@link DoubleSupplier} */ + interface OfDouble extends DoubleSupplier, PooledLambda { + /** @inheritDoc */ + PooledSupplier.OfDouble recycleOnUse(); + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 961bcb9785e9..8df0fb891f1c 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -84,6 +84,7 @@ cc_library_shared { "android_view_VelocityTracker.cpp", "android_text_AndroidCharacter.cpp", "android_text_Hyphenator.cpp", + "android_text_MeasuredText.cpp", "android_text_StaticLayout.cpp", "android_os_Debug.cpp", "android_os_GraphicsEnvironment.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f41aacd7fbeb..9e907bdf458f 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -177,6 +177,7 @@ extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_net_TrafficStats(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_Hyphenator(JNIEnv *env); +extern int register_android_text_MeasuredText(JNIEnv* env); extern int register_android_text_StaticLayout(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); @@ -1333,6 +1334,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), + REG_JNI(register_android_text_MeasuredText), REG_JNI(register_android_text_StaticLayout), REG_JNI(register_android_view_InputDevice), REG_JNI(register_android_view_KeyCharacterMap), diff --git a/core/jni/android_text_MeasuredText.cpp b/core/jni/android_text_MeasuredText.cpp new file mode 100644 index 000000000000..af9d13122201 --- /dev/null +++ b/core/jni/android_text_MeasuredText.cpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#define LOG_TAG "MeasuredText" + +#include "ScopedIcuLocale.h" +#include "unicode/locid.h" +#include "unicode/brkiter.h" +#include "utils/misc.h" +#include "utils/Log.h" +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/JNIHelp.h> +#include "core_jni_helpers.h" +#include <cstdint> +#include <vector> +#include <list> +#include <algorithm> + +#include "SkPaint.h" +#include "SkTypeface.h" +#include <hwui/MinikinSkia.h> +#include <hwui/MinikinUtils.h> +#include <hwui/Paint.h> +#include <minikin/FontCollection.h> +#include <minikin/AndroidLineBreakerHelper.h> +#include <minikin/MinikinFont.h> + +namespace android { + +static inline minikin::MeasuredTextBuilder* toBuilder(jlong ptr) { + return reinterpret_cast<minikin::MeasuredTextBuilder*>(ptr); +} + +static inline Paint* toPaint(jlong ptr) { + return reinterpret_cast<Paint*>(ptr); +} + +static inline minikin::MeasuredText* toMeasuredText(jlong ptr) { + return reinterpret_cast<minikin::MeasuredText*>(ptr); +} + +template<typename Ptr> static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast<jlong>(ptr); +} + +static void releaseMeasuredText(jlong measuredTextPtr) { + delete toMeasuredText(measuredTextPtr); +} + +// Regular JNI +static jlong nInitBuilder() { + return toJLong(new minikin::MeasuredTextBuilder()); +} + +// Regular JNI +static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jboolean isRtl) { + Paint* paint = toPaint(paintPtr); + const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); + minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); + toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), + typeface->fFontCollection, isRtl); +} + +// Regular JNI +static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jfloat width) { + toBuilder(builderPtr)->addReplacementRun(start, end, width, + toPaint(paintPtr)->getMinikinLocaleListId()); +} + +// Regular JNI +static jlong nBuildNativeMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, + jcharArray javaText) { + ScopedCharArrayRO text(env, javaText); + const minikin::U16StringPiece textBuffer(text.get(), text.size()); + + // Pass the ownership to Java. + return toJLong(toBuilder(builderPtr)->build(textBuffer).release()); +} + +// Regular JNI +static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) { + delete toBuilder(builderPtr); +} + +// CriticalNative +static jlong nGetReleaseFunc() { + return toJLong(&releaseMeasuredText); +} + +static const JNINativeMethod gMethods[] = { + // MeasuredTextBuilder native functions. + {"nInitBuilder", "()J", (void*) nInitBuilder}, + {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, + {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, + {"nBuildNativeMeasuredText", "(J[C)J", (void*) nBuildNativeMeasuredText}, + {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, + + // MeasuredText native functions. + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives +}; + +int register_android_text_MeasuredText(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/text/MeasuredText", gMethods, NELEM(gMethods)); +} + +} diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 601bd989ce42..b5c23dfa873d 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -115,6 +115,7 @@ static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, // Inputs jcharArray javaText, + jlong measuredTextPtr, jint length, jfloat firstWidth, jint firstWidthLineCount, @@ -139,39 +140,20 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, ScopedNullableIntArrayRO tabStops(env, variableTabStops); minikin::U16StringPiece u16Text(text.get(), length); - minikin::MeasuredText measuredText = builder->measureText(u16Text); + minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr); minikin::LineBreakResult result = builder->computeBreaks( - u16Text, measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, + u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, tabStops.get(), tabStops.size(), defaultTabStop); recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, recycleFlags, recycleLength, result); - env->SetFloatArrayRegion(charWidths, 0, measuredText.widths.size(), measuredText.widths.data()); - - builder->clearRuns(); + env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(), + measuredText->widths.data()); return static_cast<jint>(result.breakPoints.size()); } -// Basically similar to Paint.getTextRunAdvances but with C++ interface -// CriticalNative -static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) { - minikin::android::StaticLayoutNative* builder = toNative(nativePtr); - Paint* paint = reinterpret_cast<Paint*>(nativePaint); - const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); - minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); - builder->addStyleRun(start, end, std::move(minikinPaint), typeface->fFontCollection, isRtl); -} - -// CriticalNative -static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end, - jfloat width) { - minikin::android::StaticLayoutNative* builder = toNative(nativePtr); - Paint* paint = reinterpret_cast<Paint*>(nativePaint); - builder->addReplacementRun(start, end, width, paint->getMinikinLocaleListId()); -} - static const JNINativeMethod gMethods[] = { // Fast Natives {"nInit", "(" @@ -185,8 +167,6 @@ static const JNINativeMethod gMethods[] = { // Critical Natives {"nFinish", "(J)V", (void*) nFinish}, - {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, - {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, // Regular JNI {"nComputeLineBreaks", "(" @@ -194,6 +174,7 @@ static const JNINativeMethod gMethods[] = { // Inputs "[C" // text + "J" // MeasuredText ptr. "I" // length "F" // firstWidth "I" // firstWidthLineCount diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8c968a2a7083..5e5d59b8aaca 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -868,11 +868,40 @@ static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject token capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance()); } +static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { + Parcel* parcel = parcelForJavaObject(env, parcelObj); + if (parcel == NULL) { + doThrowNPE(env); + return 0; + } + sp<SurfaceControl> surface = SurfaceControl::readFromParcel(parcel); + if (surface == nullptr) { + return 0; + } + surface->incStrong((void *)nativeCreate); + return reinterpret_cast<jlong>(surface.get()); +} + +static void nativeWriteToParcel(JNIEnv* env, jclass clazz, + jlong nativeObject, jobject parcelObj) { + Parcel* parcel = parcelForJavaObject(env, parcelObj); + if (parcel == NULL) { + doThrowNPE(env); + return; + } + SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject); + self->writeToParcel(parcel); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJII)J", (void*)nativeCreate }, + {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", + (void*)nativeReadFromParcel }, + {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", + (void*)nativeWriteToParcel }, {"nativeRelease", "(J)V", (void*)nativeRelease }, {"nativeDestroy", "(J)V", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d9fb230f2176..09204261811d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3616,6 +3616,11 @@ <permission android:name="android.permission.ACCESS_SHORTCUTS" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to read the runtime profiles of other apps. + @hide <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_RUNTIME_PROFILES" + android:protectionLevel="signature|privileged" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/drawable/ic_logout.xml b/core/res/res/drawable/ic_logout.xml new file mode 100644 index 000000000000..c016ec818e4f --- /dev/null +++ b/core/res/res/drawable/ic_logout.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <group> + <clip-path android:pathData="M0,0h24v24H0V0z M 0,0" /> + <path + android:fillColor="#FF000000" + android:pathData="M17.0,7.0l-1.4099998,1.4099998l2.58,2.5900002l-10.17,0.0l0.0,2.0l10.17,0.0l-2.58,2.58l1.4099998,1.4200001l5.0,-5.0z"/> + <path + android:fillColor="#FF000000" + android:pathData="M4,5h8V3H4C2.9,3,2,3.9,2,5v14c0,1.1,0.9,2,2,2h8v-2H4V5z"/> + </group> +</vector> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 3b49d9dbc22b..faf3c2f1ec22 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3294,6 +3294,9 @@ and subtype in order to provide the consistent user experience in switching between IMEs and subtypes. --> <attr name="supportsSwitchingToNextInputMethod" format="boolean" /> + <!-- Specifies if an IME can only be used while a device is in VR mode or on a dedicated + device --> + <attr name="isVrOnly" format="boolean"/> <attr name="__removed2" format="boolean" /> </declare-styleable> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 4d67494be21d..e87b9d80a58e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -320,6 +320,13 @@ as multiple split APKs, each APK must have the exact same versionCode. --> <attr name="versionCode" format="integer" /> + <!-- Internal major version code. This is essentially additional high bits + for the base version code; it has no other meaning than + that higher numbers are more recent. This is not a version + number generally shown to the user, that is usually supplied + with {@link android.R.attr#versionName}. --> + <attr name="versionCodeMajor" format="integer" /> + <!-- Internal revision code. This number is the number used to determine whether one APK is more recent than another: it has no other meaning than that higher numbers are more recent. This value is only meaningful @@ -1389,6 +1396,7 @@ {@link #AndroidManifestUsesFeature uses-feature}. --> <declare-styleable name="AndroidManifest"> <attr name="versionCode" /> + <attr name="versionCodeMajor" /> <attr name="versionName" /> <attr name="revisionCode" /> <attr name="sharedUserId" /> @@ -1787,6 +1795,10 @@ <attr name="name" /> <!-- Required specific library version. --> <attr name="version" /> + <!-- Required specific library major version code. This matches + android:versionCodeMajor of the library. --> + <!-- Required specific library version. --> + <attr name="versionMajor" format="integer" /> </declare-styleable> <!-- The <code>uses-libraries</code> specifies a shared library that this diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fc649188fff6..0b38d1b1fca1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2441,10 +2441,12 @@ "users" = list of users "restart" = restart device "lockdown" = Lock down device until the user authenticates + "logout" = Logout the current user --> <string-array translatable="false" name="config_globalActionsList"> <item>power</item> <item>restart</item> + <item>logout</item> <item>bugreport</item> <item>users</item> <item>lockdown</item> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3dbb89c2a36d..d79df353f7c7 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2854,6 +2854,10 @@ <public name="compileSdkVersionCodename" /> <public name="screenReaderFocusable" /> <public name="buttonCornerRadius" /> + <public name="versionCodeMajor" /> + <public name="versionMajor" /> + <!-- @hide @SystemApi --> + <public name="isVrOnly"/> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 93bd4ec3bcf7..8958af4bffc8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -471,6 +471,9 @@ <!-- label for item that generates a bug report in the phone options dialog --> <string name="global_action_bug_report">Bug report</string> + <!-- label for item that logouts the current user --> + <string name="global_action_logout">End session</string> + <!-- Take bug report menu title [CHAR LIMIT=NONE] --> <string name="bugreport_title">Take bug report</string> <!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index feb78008da30..cbd34f9e1520 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3163,4 +3163,7 @@ <java-symbol type="string" name="unsupported_compile_sdk_check_update" /> <java-symbol type="string" name="battery_saver_warning_title" /> + + <java-symbol type="string" name="global_action_logout" /> + <java-symbol type="drawable" name="ic_logout" /> </resources> diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf Binary files differnew file mode 100644 index 000000000000..1bad6fe75090 --- /dev/null +++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx new file mode 100644 index 000000000000..0cf0f7914931 --- /dev/null +++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="0em"/> + <GlyphID id="2" name="1em"/> + <GlyphID id="3" name="3em"/> + <GlyphID id="4" name="5em"/> + <GlyphID id="5" name="7em"/> + <GlyphID id="6" name="10em"/> + <GlyphID id="7" name="50em"/> + <GlyphID id="8" name="100em"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <magicNumber value="0x5f0f3cf5"/> + <flags value="00000000 00000011"/> + <unitsPerEm value="100"/> + <created value="Fri Mar 17 07:26:00 2017"/> + <macStyle value="00000000 00000000"/> + <lowestRecPPEM value="7"/> + <fontDirectionHint value="2"/> + <glyphDataFormat value="0"/> + </head> + + <hhea> + <tableVersion value="0x00010000"/> + <ascent value="1000"/> + <descent value="-200"/> + <lineGap value="0"/> + <caretSlopeRise value="1"/> + <caretSlopeRun value="0"/> + <caretOffset value="0"/> + <reserved0 value="0"/> + <reserved1 value="0"/> + <reserved2 value="0"/> + <reserved3 value="0"/> + <metricDataFormat value="0"/> + </hhea> + + <maxp> + <tableVersion value="0x10000"/> + <maxZones value="0"/> + <maxTwilightPoints value="0"/> + <maxStorage value="0"/> + <maxFunctionDefs value="0"/> + <maxInstructionDefs value="0"/> + <maxStackElements value="0"/> + <maxSizeOfInstructions value="0"/> + <maxComponentElements value="0"/> + </maxp> + + <OS_2> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <version value="3"/> + <xAvgCharWidth value="594"/> + <usWeightClass value="400"/> + <usWidthClass value="5"/> + <fsType value="00000000 00001000"/> + <ySubscriptXSize value="650"/> + <ySubscriptYSize value="600"/> + <ySubscriptXOffset value="0"/> + <ySubscriptYOffset value="75"/> + <ySuperscriptXSize value="650"/> + <ySuperscriptYSize value="600"/> + <ySuperscriptXOffset value="0"/> + <ySuperscriptYOffset value="350"/> + <yStrikeoutSize value="50"/> + <yStrikeoutPosition value="300"/> + <sFamilyClass value="0"/> + <panose> + <bFamilyType value="0"/> + <bSerifStyle value="0"/> + <bWeight value="5"/> + <bProportion value="0"/> + <bContrast value="0"/> + <bStrokeVariation value="0"/> + <bArmStyle value="0"/> + <bLetterForm value="0"/> + <bMidline value="0"/> + <bXHeight value="0"/> + </panose> + <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/> + <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/> + <achVendID value="UKWN"/> + <fsSelection value="00000000 01000000"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <sTypoAscender value="800"/> + <sTypoDescender value="-200"/> + <sTypoLineGap value="200"/> + <usWinAscent value="1000"/> + <usWinDescent value="200"/> + <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/> + <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/> + <sxHeight value="500"/> + <sCapHeight value="700"/> + <usDefaultChar value="0"/> + <usBreakChar value="32"/> + <usMaxContext value="0"/> + </OS_2> + + <hmtx> + <mtx name=".notdef" width="50" lsb="0"/> + <mtx name="0em" width="0" lsb="0"/> + <mtx name="1em" width="100" lsb="0"/> + <mtx name="3em" width="300" lsb="0"/> + <mtx name="5em" width="500" lsb="0"/> + <mtx name="7em" width="700" lsb="0"/> + <mtx name="10em" width="1000" lsb="0"/> + <mtx name="50em" width="5000" lsb="0"/> + <mtx name="100em" width="10000" lsb="0"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0"> + <map code="0x0020" name="10em" /> + <map code="0x002e" name="10em" /> <!-- . --> + <map code="0x0043" name="100em" /> <!-- C --> + <map code="0x0049" name="1em" /> <!-- I --> + <map code="0x004c" name="50em" /> <!-- L --> + <map code="0x0056" name="5em" /> <!-- V --> + <map code="0x0058" name="10em" /> <!-- X --> + <map code="0x005f" name="0em" /> <!-- _ --> + <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR --> + <map code="0x10331" name="10em" /> + </cmap_format_12> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Font for StaticLayoutLineBreakingTest + </namerecord> + <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Regular + </namerecord> + <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Font for StaticLayoutLineBreakingTest + </namerecord> + <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True"> + SampleFont-Regular + </namerecord> + <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409"> + Regular + </namerecord> + <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409"> + SampleFont-Regular + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/res/xml/ime_meta_vr_only.xml b/core/tests/coretests/res/xml/ime_meta_vr_only.xml new file mode 100644 index 000000000000..653a8ffcb944 --- /dev/null +++ b/core/tests/coretests/res/xml/ime_meta_vr_only.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. +--> + +<input-method + xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity" + android:isVrOnly="true"> +</input-method> diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredTextTest.java new file mode 100644 index 000000000000..ddef0c651b31 --- /dev/null +++ b/core/tests/coretests/src/android/text/MeasuredTextTest.java @@ -0,0 +1,168 @@ +/* + * 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 android.text; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +import android.content.Context; +import android.graphics.Typeface; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class MeasuredTextTest { + private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR; + private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL; + + private static final TextPaint PAINT = new TextPaint(); + static { + // The test font has following coverage and width. + // U+0020: 10em + // U+002E (.): 10em + // U+0043 (C): 100em + // U+0049 (I): 1em + // U+004C (L): 50em + // U+0056 (V): 5em + // U+0058 (X): 10em + // U+005F (_): 0em + // U+FFFD (invalid surrogate will be replaced to this): 7em + // U+10331 (\uD800\uDF31): 10em + Context context = InstrumentationRegistry.getTargetContext(); + PAINT.setTypeface(Typeface.createFromAsset(context.getAssets(), + "fonts/StaticLayoutLineBreakingTestFont.ttf")); + PAINT.setTextSize(1.0f); // Make 1em == 1px. + } + + private String charsToString(char[] chars) { + return (new StringBuilder()).append(chars).toString(); + } + + @Test + public void buildForBidi() { + MeasuredText mt = null; + + mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null); + assertNotNull(mt); + assertNotNull(mt.getChars()); + assertEquals("XXX", charsToString(mt.getChars())); + assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir()); + assertNotNull(mt.getDirections(0, 3)); + assertEquals(0, mt.getWholeWidth(), 0); + assertEquals(0, mt.getWidths().size()); + assertEquals(0, mt.getSpanEndCache().size()); + assertEquals(0, mt.getFontMetrics().size()); + assertEquals(0, mt.getNativePtr()); + + // Recycle it + MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt); + assertEquals(mt2, mt); + assertNotNull(mt2.getChars()); + assertEquals("VVV", charsToString(mt.getChars())); + assertNotNull(mt2.getDirections(0, 3)); + assertEquals(0, mt2.getWholeWidth(), 0); + assertEquals(0, mt2.getWidths().size()); + assertEquals(0, mt2.getSpanEndCache().size()); + assertEquals(0, mt2.getFontMetrics().size()); + assertEquals(0, mt2.getNativePtr()); + + mt2.recycle(); + } + + @Test + public void buildForMeasurement() { + MeasuredText mt = null; + + mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null); + assertNotNull(mt); + assertNotNull(mt.getChars()); + assertEquals("XXX", charsToString(mt.getChars())); + assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir()); + assertNotNull(mt.getDirections(0, 3)); + assertEquals(30, mt.getWholeWidth(), 0); + assertEquals(3, mt.getWidths().size()); + assertEquals(10, mt.getWidths().get(0), 0); + assertEquals(10, mt.getWidths().get(1), 0); + assertEquals(10, mt.getWidths().get(2), 0); + assertEquals(0, mt.getSpanEndCache().size()); + assertEquals(0, mt.getFontMetrics().size()); + assertEquals(0, mt.getNativePtr()); + + // Recycle it + MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt); + assertEquals(mt2, mt); + assertNotNull(mt2.getChars()); + assertEquals("VVV", charsToString(mt.getChars())); + assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir()); + assertNotNull(mt2.getDirections(0, 3)); + assertEquals(15, mt2.getWholeWidth(), 0); + assertEquals(3, mt2.getWidths().size()); + assertEquals(5, mt2.getWidths().get(0), 0); + assertEquals(5, mt2.getWidths().get(1), 0); + assertEquals(5, mt2.getWidths().get(2), 0); + assertEquals(0, mt2.getSpanEndCache().size()); + assertEquals(0, mt2.getFontMetrics().size()); + assertEquals(0, mt2.getNativePtr()); + + mt2.recycle(); + } + + @Test + public void buildForStaticLayout() { + MeasuredText mt = null; + + mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null); + assertNotNull(mt); + assertNotNull(mt.getChars()); + assertEquals("XXX", charsToString(mt.getChars())); + assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir()); + assertNotNull(mt.getDirections(0, 3)); + assertEquals(0, mt.getWholeWidth(), 0); + assertEquals(0, mt.getWidths().size()); + assertEquals(1, mt.getSpanEndCache().size()); + assertEquals(3, mt.getSpanEndCache().get(0)); + assertNotEquals(0, mt.getFontMetrics().size()); + assertNotEquals(0, mt.getNativePtr()); + + // Recycle it + MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt); + assertEquals(mt2, mt); + assertNotNull(mt2.getChars()); + assertEquals("VVV", charsToString(mt.getChars())); + assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir()); + assertNotNull(mt2.getDirections(0, 3)); + assertEquals(0, mt2.getWholeWidth(), 0); + assertEquals(0, mt2.getWidths().size()); + assertEquals(1, mt2.getSpanEndCache().size()); + assertEquals(4, mt2.getSpanEndCache().get(0)); + assertNotEquals(0, mt2.getFontMetrics().size()); + assertNotEquals(0, mt2.getNativePtr()); + + mt2.recycle(); + } + + @Test + public void testFor70146381() { + MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java index 13cef5290a4e..e3c91e64f69c 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java @@ -68,6 +68,17 @@ public class InputMethodInfoTest { assertThat(clone.supportsSwitchingToNextInputMethod(), is(true)); } + @Test + public void testIsVrOnly() throws Exception { + final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only); + + assertThat(imi.isVrOnly(), is(true)); + + final InputMethodInfo clone = cloneViaParcel(imi); + + assertThat(clone.isVrOnly(), is(true)); + } + private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes) throws Exception { final Context context = InstrumentationRegistry.getContext(); diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java index 515e5580a73c..9061c4407685 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java @@ -38,6 +38,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe private static final String DUMMY_SETTING_ACTIVITY_NAME = ""; private static final boolean DUMMY_IS_AUX_IME = false; private static final boolean DUMMY_FORCE_DEFAULT = false; + private static final boolean DUMMY_IS_VR_IME = false; private static final int DUMMY_IS_DEFAULT_RES_ID = 0; private static final String SYSTEM_LOCALE = "en_US"; private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; @@ -75,7 +76,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe } final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME, DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID, - DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod); + DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, DUMMY_IS_VR_IME); if (subtypes == null) { items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi, NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE)); @@ -111,7 +112,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe .build()); final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME, DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID, - DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */); + DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */, + DUMMY_IS_VR_IME); return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale, systemLocale); } diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 845acc0fc0b2..e2f02df62b30 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -205,7 +205,13 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) { paint = &tmpPaint; } - renderNode->getLayerSurface()->draw(canvas, 0, 0, paint); + + // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so + // we need to restrict the portion of the surface drawn to the size of the renderNode. + SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); + SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); + canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), + bounds, bounds, paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index d5fe7f4fa6aa..4ba368f35bfd 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -161,14 +161,18 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, bool wideColorGamut) { + // compute the size of the surface (i.e. texture) to be allocated for this layer + const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE; + const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE; + SkSurface* layer = node->getLayerSurface(); - if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) { + if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { SkImageInfo info; if (wideColorGamut) { - info = SkImageInfo::Make(node->getWidth(), node->getHeight(), kRGBA_F16_SkColorType, + info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType, kPremul_SkAlphaType); } else { - info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight()); + info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight); } SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 7dd271f5daf4..c7f57fee1d5a 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -460,15 +460,15 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { ProjectionLayer(int* drawCounter) : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr) , mDrawCounter(drawCounter) {} - void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override { + virtual sk_sp<SkImage> onNewImageSnapshot() override { EXPECT_EQ(3, (*mDrawCounter)++); EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X, 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas())); + return nullptr; } SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); } sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; } - sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; } void onCopyOnWrite(ContentChangeMode) override {} int* mDrawCounter; }; diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index aaf18e7f935a..ba29d2daaa0e 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -423,6 +423,12 @@ public class MtpDatabase implements AutoCloseable { } } + private void doScanDirectory(String path) { + String[] scanPath; + scanPath = new String[] { path }; + mMediaScanner.scanDirectories(scanPath); + } + private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException { String where; String[] whereArgs; diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index fe2a939b58a6..4e8c72bae9b9 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -55,6 +55,7 @@ using namespace android; static jmethodID method_beginSendObject; static jmethodID method_endSendObject; +static jmethodID method_doScanDirectory; static jmethodID method_getObjectList; static jmethodID method_getNumObjects; static jmethodID method_getSupportedPlaybackFormats; @@ -119,6 +120,8 @@ public: MtpObjectFormat format, bool succeeded); + virtual void doScanDirectory(const char* path); + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); @@ -265,6 +268,16 @@ void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, checkAndClearExceptionFromCallback(env, __FUNCTION__); } +void MyMtpDatabase::doScanDirectory(const char* path) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jstring pathStr = env->NewStringUTF(path); + env->CallVoidMethod(mDatabase, method_doScanDirectory, pathStr); + + if (pathStr) + env->DeleteLocalRef(pathStr); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { @@ -1311,6 +1324,11 @@ int register_android_mtp_MtpDatabase(JNIEnv *env) ALOGE("Can't find endSendObject"); return -1; } + method_doScanDirectory = env->GetMethodID(clazz, "doScanDirectory", "(Ljava/lang/String;)V"); + if (method_doScanDirectory == NULL) { + ALOGE("Can't find doScanDirectory"); + return -1; + } method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I"); if (method_getObjectList == NULL) { ALOGE("Can't find getObjectList"); diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 4a771ebdf565..8b01aef81c67 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -138,6 +138,7 @@ public class DefaultContainerService extends IntentService { ret.packageName = pkg.packageName; ret.splitNames = pkg.splitNames; ret.versionCode = pkg.versionCode; + ret.versionCodeMajor = pkg.versionCodeMajor; ret.baseRevisionCode = pkg.baseRevisionCode; ret.splitRevisionCodes = pkg.splitRevisionCodes; ret.installLocation = pkg.installLocation; diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java index 2c2641079953..8055caaad536 100644 --- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java @@ -227,7 +227,7 @@ public class HelpUtils { // cache the version code PackageInfo info = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); - sCachedVersionCode = Integer.toString(info.versionCode); + sCachedVersionCode = Long.toString(info.getLongVersionCode()); // append the version code to the uri builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index 5b39ee4b57f1..c3ff617bfaa7 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -118,7 +118,7 @@ public class CachedBluetoothDeviceManager { public synchronized void clearNonBondedDevices() { for (int i = mCachedDevices.size() - 1; i >= 0; i--) { CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); - if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) { + if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE) { mCachedDevices.remove(i); } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java index 820231ef76c4..85b04c8fdf15 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java @@ -159,14 +159,14 @@ public class AppRestrictionsHelperTest extends BaseTest { for (String pkg : defaultImes) { final ResolveInfo ri = createResolveInfoForSystemApp(pkg); final InputMethodInfo inputMethodInfo = new InputMethodInfo( - ri, false, null, null, 0, true, true); + ri, false, null, null, 0, true, true, false); inputMethods.add(inputMethodInfo); addInstalledApp(ri); } for (String pkg : otherImes) { final ResolveInfo ri = createResolveInfoForSystemApp(pkg); final InputMethodInfo inputMethodInfo = new InputMethodInfo( - ri, false, null, null, 0, false, true); + ri, false, null, null, 0, false, true, false); inputMethods.add(inputMethodInfo); addInstalledApp(ri); } diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 863f17b432f2..fac254a0083a 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<RelativeLayout +<com.android.systemui.HardwareUiLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" @@ -45,4 +45,4 @@ </LinearLayout> </RelativeLayout> -</RelativeLayout> +</com.android.systemui.HardwareUiLayout> diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 00e8b1a44e47..3b54e11447b7 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -19,6 +19,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import android.app.ActivityManager; import android.app.Dialog; import android.app.WallpaperManager; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -112,11 +113,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist"; private static final String GLOBAL_ACTION_KEY_ASSIST = "assist"; private static final String GLOBAL_ACTION_KEY_RESTART = "restart"; + private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout"; private final Context mContext; private final GlobalActionsManager mWindowManagerFuncs; private final AudioManager mAudioManager; private final IDreamManager mDreamManager; + private final DevicePolicyManager mDevicePolicyManager; private ArrayList<Action> mItems; private ActionsDialog mDialog; @@ -132,6 +135,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private boolean mIsWaitingForEcmExit = false; private boolean mHasTelephony; private boolean mHasVibrator; + private boolean mHasLogoutButton; private final boolean mShowSilentToggle; private final EmergencyAffordanceManager mEmergencyAffordanceManager; @@ -144,6 +148,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mDreamManager = IDreamManager.Stub.asInterface( ServiceManager.getService(DreamService.DREAM_SERVICE)); + mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -289,6 +295,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, R.array.config_globalActionsList); ArraySet<String> addedKeys = new ArraySet<String>(); + mHasLogoutButton = false; for (int i = 0; i < defaultActions.length; i++) { String actionKey = defaultActions[i]; if (addedKeys.contains(actionKey)) { @@ -325,6 +332,12 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mItems.add(getAssistAction()); } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { mItems.add(new RestartAction()); + } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) { + if (mDevicePolicyManager.isLogoutButtonEnabled() + && getCurrentUser().id != UserHandle.USER_SYSTEM) { + mItems.add(new LogoutAction()); + mHasLogoutButton = true; + } } else { Log.e(TAG, "Invalid global action key " + actionKey); } @@ -490,6 +503,37 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } + private final class LogoutAction extends SinglePressAction { + private LogoutAction() { + super(R.drawable.ic_logout, R.string.global_action_logout); + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return false; + } + + @Override + public void onPress() { + // Add a little delay before executing, to give the dialog a chance to go away before + // switching user + mHandler.postDelayed(() -> { + try { + ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); + ActivityManager.getService().stopUser(getCurrentUser().id, true /*force*/, + null); + } catch (RemoteException re) { + Log.e(TAG, "Couldn't logout user " + re); + } + }, 500); + } + } + private Action getSettingsAction() { return new SinglePressAction(R.drawable.ic_settings, R.string.global_action_settings) { @@ -764,7 +808,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, public View getView(int position, View convertView, ViewGroup parent) { Action action = getItem(position); View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); - if (position == 2) { + // When there is no logout button, only power off and restart should be in white + // background, thus setting division view at third item; with logout button being the + // third item, set the division view at fourth item instead. + if (position == (mHasLogoutButton ? 3 : 2)) { HardwareUiLayout.get(parent).setDivisionView(view); } return view; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 09fe579b83c6..f41cb293aeb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -615,6 +615,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, .addCategory(Intent.CATEGORY_BROWSABLE) .addCategory("unique:" + System.currentTimeMillis()) .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName) + .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff)) .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode) .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index f16d7b8d3745..0d41e2029086 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -19,9 +19,6 @@ package com.android.systemui.volume; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE; import android.accessibilityservice.AccessibilityServiceInfo; @@ -45,7 +42,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; -import android.provider.Settings; import android.provider.Settings.Global; import android.util.Log; import android.util.Slog; @@ -71,7 +67,6 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.systemui.Dependency; -import com.android.systemui.HardwareBgDrawable; import com.android.systemui.HardwareUiLayout; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -79,7 +74,6 @@ import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.plugins.VolumeDialogController.StreamState; -import com.android.systemui.util.leak.RotationUtils; import java.io.PrintWriter; import java.util.ArrayList; @@ -103,13 +97,9 @@ public class VolumeDialogImpl implements VolumeDialog { private final VolumeDialogController mController; private Window mWindow; - //private HardwareUiLayout mHardwareLayout; + private HardwareUiLayout mHardwareLayout; private CustomDialog mDialog; private ViewGroup mDialogView; - private boolean mEdgeBleed; - private boolean mRoundedDivider; - private HardwareBgDrawable mBackground; - private int mRotation = ROTATION_NONE; private ViewGroup mDialogRowsView; private ViewGroup mDialogContentView; private final List<VolumeRow> mRows = new ArrayList<>(); @@ -121,8 +111,6 @@ public class VolumeDialogImpl implements VolumeDialog { private final Accessibility mAccessibility = new Accessibility(); private final ColorStateList mActiveSliderTint; private final ColorStateList mInactiveSliderTint; - private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; - private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; private boolean mShowing; private boolean mShowA11yStream; @@ -193,16 +181,8 @@ public class VolumeDialogImpl implements VolumeDialog { return true; } }); - - mEdgeBleed = Settings.Secure.getInt(mContext.getContentResolver(), - EDGE_BLEED, 0) != 0; - mRoundedDivider = Settings.Secure.getInt(mContext.getContentResolver(), - ROUNDED_DIVIDER, 1) != 0; - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, mContext); - mDialogView.setBackground(mBackground); - //mHardwareLayout = HardwareUiLayout.get(mDialogView); - //mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); + mHardwareLayout = HardwareUiLayout.get(mDialogView); + mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content); mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows); @@ -230,25 +210,6 @@ public class VolumeDialogImpl implements VolumeDialog { updateRowsH(getActiveRow()); } - private int getEdgePadding() { - return mContext.getResources().getDimensionPixelSize(R.dimen.edge_margin); - } - - private void updateEdgeMargin(int edge) { - if (mDialogView != null) { - mRotation = RotationUtils.getRotation(mContext); - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mDialogView.setLayoutParams(params); - } - } - private ColorStateList loadColorStateList(int colorResId) { return ColorStateList.valueOf(mContext.getColor(colorResId)); } @@ -428,11 +389,11 @@ public class VolumeDialogImpl implements VolumeDialog { rescheduleTimeoutH(); if (mShowing) return; mShowing = true; - mDialogView.setTranslationY(getAnimTranslation()); - mDialogView.setAlpha(0); - mDialogView.animate() + mHardwareLayout.setTranslationX(getAnimTranslation()); + mHardwareLayout.setAlpha(0); + mHardwareLayout.animate() .alpha(1) - .translationY(0) + .translationX(0) .setDuration(300) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(() -> { @@ -471,17 +432,16 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.SHOW); if (!mShowing) return; mShowing = false; - mDialogView.setTranslationX(0); - mDialogView.setAlpha(1); - mDialogView.animate() + mHardwareLayout.setTranslationX(0); + mHardwareLayout.setAlpha(1); + mHardwareLayout.animate() .alpha(0) .translationX(getAnimTranslation()) .setDuration(300) .withEndAction(() -> mDialog.dismiss()) .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) .start(); - if (mAccessibilityMgr.isObservedEventType( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) { + if (mAccessibilityMgr.isEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); event.setPackageName(mContext.getPackageName()); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 4f1f4d738afc..968e08b00663 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5023,6 +5023,11 @@ message MetricsEvent { // OS: P CONNECTION_DEVICE_ADVANCED = 1264; + // OPEN: Settings > Security > Screen lock gear icon + // CATEGORY: SETTINGS + // OS: P + SCREEN_LOCK_SETTINGS = 1265; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/backup/java/com/android/server/backup/FileMetadata.java b/services/backup/java/com/android/server/backup/FileMetadata.java index 5465609fdfbd..3d260cba4a16 100644 --- a/services/backup/java/com/android/server/backup/FileMetadata.java +++ b/services/backup/java/com/android/server/backup/FileMetadata.java @@ -36,7 +36,7 @@ public class FileMetadata { public long mode; // e.g. 0666 (actually int) public long mtime; // last mod time, UTC time_t (actually int) public long size; // bytes of content - public int version; // App version. + public long version; // App version. public boolean hasApk; // Whether backup file contains apk. @Override diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index f658f22d8548..2d2993dced69 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -99,10 +99,10 @@ public class PackageManagerBackupAgent extends BackupAgent { // For compactness we store the SHA-256 hash of each app's Signatures // rather than the Signature blocks themselves. public class Metadata { - public int versionCode; + public long versionCode; public ArrayList<byte[]> sigHashes; - Metadata(int version, ArrayList<byte[]> hashes) { + Metadata(long version, ArrayList<byte[]> hashes) { versionCode = version; sigHashes = hashes; } @@ -206,7 +206,7 @@ public class PackageManagerBackupAgent extends BackupAgent { homeInfo = mPackageManager.getPackageInfo(home.getPackageName(), PackageManager.GET_SIGNATURES); homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); - homeVersion = homeInfo.versionCode; + homeVersion = homeInfo.getLongVersionCode(); homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures); } catch (NameNotFoundException e) { Slog.w(TAG, "Can't access preferred home info"); @@ -287,7 +287,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // metadata again. In either case, take it out of mExisting so that // we don't consider it deleted later. mExisting.remove(packName); - if (info.versionCode == mStateVersions.get(packName).versionCode) { + if (info.getLongVersionCode() == mStateVersions.get(packName).versionCode) { continue; } } @@ -309,13 +309,18 @@ public class PackageManagerBackupAgent extends BackupAgent { // marshal the version code in a canonical form outputBuffer.reset(); - outputBufferStream.writeInt(info.versionCode); + if (info.versionCodeMajor != 0) { + outputBufferStream.writeInt(Integer.MIN_VALUE); + outputBufferStream.writeLong(info.getLongVersionCode()); + } else { + outputBufferStream.writeInt(info.versionCode); + } writeSignatureHashArray(outputBufferStream, BackupUtils.hashSignatureArray(info.signatures)); if (DEBUG) { Slog.v(TAG, "+ writing metadata for " + packName - + " version=" + info.versionCode + + " version=" + info.getLongVersionCode() + " entityLen=" + outputBuffer.size()); } @@ -409,7 +414,13 @@ public class PackageManagerBackupAgent extends BackupAgent { } } else { // it's a file metadata record - int versionCode = inputBufferStream.readInt(); + int versionCodeInt = inputBufferStream.readInt(); + long versionCode; + if (versionCodeInt == Integer.MIN_VALUE) { + versionCode = inputBufferStream.readLong(); + } else { + versionCode = versionCodeInt; + } ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); if (DEBUG) { Slog.i(TAG, " read metadata for " + key @@ -561,7 +572,13 @@ public class PackageManagerBackupAgent extends BackupAgent { // The global metadata was last; now read all the apps while (true) { pkg = in.readUTF(); - int versionCode = in.readInt(); + int versionCodeInt = in.readInt(); + long versionCode; + if (versionCodeInt == Integer.MIN_VALUE) { + versionCode = in.readLong(); + } else { + versionCode = versionCodeInt; + } if (!ignoreExisting) { mExisting.add(pkg); @@ -609,7 +626,12 @@ public class PackageManagerBackupAgent extends BackupAgent { // now write all the app names + versions for (PackageInfo pkg : pkgs) { out.writeUTF(pkg.packageName); - out.writeInt(pkg.versionCode); + if (pkg.versionCodeMajor != 0) { + out.writeInt(Integer.MIN_VALUE); + out.writeLong(pkg.getLongVersionCode()); + } else { + out.writeInt(pkg.versionCode); + } } out.flush(); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index b538c6d4e472..5884dc536080 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -495,14 +495,14 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { return; } - if (metaInfo.versionCode > mCurrentPackage.versionCode) { + if (metaInfo.versionCode > mCurrentPackage.getLongVersionCode()) { // Data is from a "newer" version of the app than we have currently // installed. If the app has not declared that it is prepared to // handle this case, we do not attempt the restore. if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { String message = "Source version " + metaInfo.versionCode - + " > installed version " + mCurrentPackage.versionCode; + + " > installed version " + mCurrentPackage.getLongVersionCode(); Slog.w(TAG, "Package " + pkgName + ": " + message); Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION, @@ -522,7 +522,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } else { if (DEBUG) { Slog.v(TAG, "Source version " + metaInfo.versionCode - + " > installed version " + mCurrentPackage.versionCode + + " > installed version " + mCurrentPackage.getLongVersionCode() + " but restoreAnyVersion"); } Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, @@ -543,7 +543,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { Slog.v(TAG, "Package " + pkgName + " restore version [" + metaInfo.versionCode + "] is compatible with installed version [" - + mCurrentPackage.versionCode + "]"); + + mCurrentPackage.getLongVersionCode() + "]"); } // Reset per-package preconditions and fire the appropriate next state @@ -635,7 +635,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } // Guts of a key/value restore operation - void initiateOneRestore(PackageInfo app, int appVersionCode) { + void initiateOneRestore(PackageInfo app, long appVersionCode) { final String packageName = app.packageName; if (DEBUG) { diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java index 734fa1d6cd7b..010684e86b55 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java +++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java @@ -56,6 +56,8 @@ public class BackupManagerMonitorUtils { pkg.packageName); bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION, pkg.versionCode); + bundle.putLong(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION, + pkg.getLongVersionCode()); } if (extras != null) { bundle.putAll(extras); diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java index b3e20dcfb2a6..a731fc9a0932 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java @@ -97,7 +97,7 @@ public class FullBackupUtils { printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); printer.println(pkg.packageName); - printer.println(Integer.toString(pkg.versionCode)); + printer.println(Long.toString(pkg.getLongVersionCode())); printer.println(Integer.toString(Build.VERSION.SDK_INT)); String installerName = packageManager.getInstallerPackageName(pkg.packageName); diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java index 2910ba2ee2f1..ff9cb5696c77 100644 --- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java +++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java @@ -422,7 +422,7 @@ public class TarBackupReader { LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null); policy = RestorePolicy.ACCEPT; - } else if (pkgInfo.versionCode >= info.version) { + } else if (pkgInfo.getLongVersionCode() >= info.version) { Slog.i(TAG, "Sig + version match; taking data"); policy = RestorePolicy.ACCEPT; mMonitor = BackupManagerMonitorUtils.monitorEvent( @@ -439,7 +439,7 @@ public class TarBackupReader { Slog.i(TAG, "Data version " + info.version + " is newer than installed " + "version " - + pkgInfo.versionCode + + pkgInfo.getLongVersionCode() + " - requiring apk"); policy = RestorePolicy.ACCEPT_IF_APK; } else { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index f2f01cfa19b0..d44fe4dbc450 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -21,6 +21,7 @@ import static com.android.internal.util.CollectionUtils.size; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; +import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; import android.Manifest; import android.annotation.CheckResult; @@ -69,6 +70,7 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.notification.NotificationAccessConfirmationActivityContract; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.SystemService; @@ -440,32 +442,35 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return; } - Binder.withCleanCallingIdentity(() -> { - try { - if (containsEither(packageInfo.requestedPermissions, - Manifest.permission.RUN_IN_BACKGROUND, - Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) { - mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName); - } else { - mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName); - } - } catch (RemoteException e) { - /* ignore - local call */ - } + Binder.withCleanCallingIdentity(obtainRunnable(CompanionDeviceManagerService:: + updateSpecialAccessPermissionAsSystem, this, packageInfo).recycleOnUse()); + } - NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext()); + private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) { + try { if (containsEither(packageInfo.requestedPermissions, - Manifest.permission.USE_DATA_IN_BACKGROUND, - Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) { - networkPolicyManager.addUidPolicy( - packageInfo.applicationInfo.uid, - NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + android.Manifest.permission.RUN_IN_BACKGROUND, + android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) { + mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName); } else { - networkPolicyManager.removeUidPolicy( - packageInfo.applicationInfo.uid, - NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName); } - }); + } catch (RemoteException e) { + /* ignore - local call */ + } + + NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext()); + if (containsEither(packageInfo.requestedPermissions, + android.Manifest.permission.USE_DATA_IN_BACKGROUND, + android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) { + networkPolicyManager.addUidPolicy( + packageInfo.applicationInfo.uid, + NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + } else { + networkPolicyManager.removeUidPolicy( + packageInfo.applicationInfo.uid, + NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + } } private static <T> boolean containsEither(T[] array, T a, T b) { @@ -474,17 +479,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Nullable private PackageInfo getPackageInfo(String packageName, int userId) { - return Binder.withCleanCallingIdentity(() -> { + return Binder.withCleanCallingIdentity(PooledLambda.obtainSupplier((context, pkg, id) -> { try { - return getContext().getPackageManager().getPackageInfoAsUser( - packageName, + return context.getPackageManager().getPackageInfoAsUser( + pkg, PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS, - userId); + id); } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e); + Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + pkg, e); return null; } - }); + }, getContext(), packageName, userId).recycleOnUse()); } private void recordAssociation(String priviledgedPackage, String deviceAddress) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5e67396a3684..399760d492d6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4522,9 +4522,18 @@ public class ActivityManagerService extends IActivityManager.Stub userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivity", null); // TODO: Switch to user app stacks here. - return mActivityStartController.startActivityMayWait(caller, -1, callingPackage, - intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, - profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser"); + return mActivityStartController.obtainStarter(intent, "startActivityAsUser") + .setCaller(caller) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setResultTo(resultTo) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setStartFlags(startFlags) + .setProfilerInfo(profilerInfo) + .setMayWait(bOptions, userId) + .execute(); + } @Override @@ -4585,11 +4594,17 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: Switch to user app stacks here. try { - int ret = mActivityStartController.startActivityMayWait(null, targetUid, - targetPackage, intent, resolvedType, null, null, resultTo, resultWho, - requestCode, startFlags, null, null, null, bOptions, ignoreTargetSecurity, - userId, null, "startActivityAsCaller"); - return ret; + return mActivityStartController.obtainStarter(intent, "startActivityAsCaller") + .setCallingUid(targetUid) + .setCallingPackage(targetPackage) + .setResolvedType(resolvedType) + .setResultTo(resultTo) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setStartFlags(startFlags) + .setMayWait(bOptions, userId) + .setIgnoreTargetSecurity(ignoreTargetSecurity) + .execute(); } catch (SecurityException e) { // XXX need to figure out how to propagate to original app. // A SecurityException here is generally actually a fault of the original @@ -4615,9 +4630,18 @@ public class ActivityManagerService extends IActivityManager.Stub userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null); WaitResult res = new WaitResult(); // TODO: Switch to user app stacks here. - mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent, - resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, - profilerInfo, res, null, bOptions, false, userId, null, "startActivityAndWait"); + mActivityStartController.obtainStarter(intent, "startActivityAndWait") + .setCaller(caller) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setResultTo(resultTo) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setStartFlags(startFlags) + .setMayWait(bOptions, userId) + .setProfilerInfo(profilerInfo) + .setWaitResult(res) + .execute(); return res; } @@ -4629,10 +4653,17 @@ public class ActivityManagerService extends IActivityManager.Stub userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null); // TODO: Switch to user app stacks here. - int ret = mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent, - resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null, - config, bOptions, false, userId, null, "startActivityWithConfig"); - return ret; + return mActivityStartController.obtainStarter(intent, "startActivityWithConfig") + .setCaller(caller) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setResultTo(resultTo) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setStartFlags(startFlags) + .setGlobalConfiguration(config) + .setMayWait(bOptions, userId) + .execute(); } @Override @@ -4678,9 +4709,16 @@ public class ActivityManagerService extends IActivityManager.Stub userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false, ALLOW_FULL_ONLY, "startVoiceActivity", null); // TODO: Switch to user app stacks here. - return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage, - intent, resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo, - null, null, bOptions, false, userId, null, "startVoiceActivity"); + return mActivityStartController.obtainStarter(intent, "startVoiceActivity") + .setCallingUid(callingUid) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setVoiceSession(session) + .setVoiceInteractor(interactor) + .setStartFlags(startFlags) + .setProfilerInfo(profilerInfo) + .setMayWait(bOptions, userId) + .execute(); } @Override @@ -4689,9 +4727,13 @@ public class ActivityManagerService extends IActivityManager.Stub enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()"); userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false, ALLOW_FULL_ONLY, "startAssistantActivity", null); - return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage, - intent, resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions, - false, userId, null, "startAssistantActivity"); + + return mActivityStartController.obtainStarter(intent, "startAssistantActivity") + .setCallingUid(callingUid) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setMayWait(bOptions, userId) + .execute(); } @Override @@ -4731,9 +4773,12 @@ public class ActivityManagerService extends IActivityManager.Stub intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setComponent(recentsComponent); intent.putExtras(options); - return mActivityStartController.startActivityMayWait(null, recentsUid, - recentsPackage, intent, null, null, null, null, null, 0, 0, null, null, - null, activityOptions, false, userId, null, "startRecentsActivity"); + + return mActivityStartController.obtainStarter(intent, "startRecentsActivity") + .setCallingUid(recentsUid) + .setCallingPackage(recentsPackage) + .setMayWait(activityOptions, userId) + .execute(); } } finally { Binder.restoreCallingIdentity(origId); @@ -4906,11 +4951,22 @@ public class ActivityManagerService extends IActivityManager.Stub } final long origId = Binder.clearCallingIdentity(); - int res = mActivityStartController.startActivity(r.app.thread, intent, - null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null, - null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1, - r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options, - false, false, null, null, "startNextMatchingActivity"); + // TODO(b/64750076): Check if calling pid should really be -1. + final int res = mActivityStartController + .obtainStarter(intent, "startNextMatchingActivity") + .setCaller(r.app.thread) + .setResolvedType(r.resolvedType) + .setActivityInfo(aInfo) + .setResultTo(resultTo != null ? resultTo.appToken : null) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setCallingPid(-1) + .setCallingUid(r.launchedFromUid) + .setCallingPackage(r.launchedFromPackage) + .setRealCallingPid(-1) + .setRealCallingUid(r.launchedFromUid) + .setActivityOptions(options) + .execute(); Binder.restoreCallingIdentity(origId); r.finishing = wasFinishing; @@ -14542,7 +14598,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId()); if (pi != null) { - sb.append(" v").append(pi.versionCode); + sb.append(" v").append(pi.getLongVersionCode()); if (pi.versionName != null) { sb.append(" (").append(pi.versionName).append(")"); } @@ -23824,7 +23880,13 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public boolean startUserInBackground(final int userId) { - return mUserController.startUser(userId, /* foreground */ false); + return startUserInBackgroundWithListener(userId, null); + } + + @Override + public boolean startUserInBackgroundWithListener(final int userId, + @Nullable IProgressListener unlockListener) { + return mUserController.startUser(userId, /* foreground */ false, unlockListener); } @Override diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index af4d3f8e4ba2..24058901a2aa 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3899,12 +3899,19 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( destIntent.getComponent(), 0, srec.userId); - int res = mService.getActivityStartController().startActivity( - srec.app.thread, destIntent, null /*ephemeralIntent*/, null, aInfo, - null /*rInfo*/, null, null, parent.appToken, null, 0, -1, - parent.launchedFromUid, parent.launchedFromPackage, -1, - parent.launchedFromUid, 0, null, false, true, null, null, - "navigateUpTo"); + // TODO(b/64750076): Check if calling pid should really be -1. + final int res = mService.getActivityStartController() + .obtainStarter(destIntent, "navigateUpTo") + .setCaller(srec.app.thread) + .setActivityInfo(aInfo) + .setResultTo(parent.appToken) + .setCallingPid(-1) + .setCallingUid(parent.launchedFromUid) + .setCallingPackage(parent.launchedFromPackage) + .setRealCallingPid(-1) + .setRealCallingUid(parent.launchedFromUid) + .setComponentSpecified(true) + .execute(); foundParentInTask = res == ActivityManager.START_SUCCESS; } catch (RemoteException e) { foundParentInTask = false; diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java index 317a68f9b09c..a97b93ca01ba 100644 --- a/services/core/java/com/android/server/am/ActivityStartController.java +++ b/services/core/java/com/android/server/am/ActivityStartController.java @@ -25,8 +25,6 @@ import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY; import android.app.ActivityOptions; import android.app.IApplicationThread; -import android.app.ProfilerInfo; -import android.app.WaitResult; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Intent; @@ -34,7 +32,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; import android.os.FactoryTest; @@ -43,12 +40,10 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.provider.Settings; -import android.service.voice.IVoiceInteractionSession; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch; -import com.android.internal.app.IVoiceInteractor; import com.android.server.am.ActivityStarter.DefaultFactory; import com.android.server.am.ActivityStarter.Factory; @@ -72,7 +67,6 @@ public class ActivityStartController { private final ActivityManagerService mService; private final ActivityStackSupervisor mSupervisor; - private final ActivityStartInterceptor mInterceptor; /** Last home activity record we attempted to start. */ private ActivityRecord mLastHomeActivityStartRecord; @@ -115,7 +109,9 @@ public class ActivityStartController { private ActivityStarter mLastStarter; ActivityStartController(ActivityManagerService service) { - this(service, service.mStackSupervisor, new DefaultFactory()); + this(service, service.mStackSupervisor, + new DefaultFactory(service, service.mStackSupervisor, + new ActivityStartInterceptor(service, service.mStackSupervisor))); } @VisibleForTesting @@ -124,38 +120,20 @@ public class ActivityStartController { mService = service; mSupervisor = supervisor; mHandler = new StartHandler(mService.mHandlerThread.getLooper()); - mInterceptor = new ActivityStartInterceptor(mService, mSupervisor); mFactory = factory; + mFactory.setController(this); } /** - * Retrieves a starter to be used for a new start request. The starter will be added to the - * active starters list. - * - * TODO(b/64750076): This should be removed when {@link #obtainStarter} is implemented. At that - * time, {@link ActivityStarter#execute} will be able to handle cleaning up the starter's - * internal references. + * @return A starter to configure and execute starting an activity. It is valid until after + * {@link ActivityStarter#execute} is invoked. At that point, the starter should be + * considered invalid and no longer modified or used. */ - private ActivityStarter createStarter() { - mLastStarter = mFactory.getStarter(this, mService, mService.mStackSupervisor, mInterceptor); - return mLastStarter; - } + ActivityStarter obtainStarter(Intent intent, String reason) { + final ActivityStarter starter = mFactory.obtainStarter(); + mLastStarter = starter; - /** - * TODO(b/64750076): Remove once we directly expose starter interface to callers through - * {@link #obtainStarter}. - */ - int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, - String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, - String callingPackage, int realCallingPid, int realCallingUid, int startFlags, - ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, - ActivityRecord[] outActivity, TaskRecord inTask, String reason) { - return createStarter().startActivityLocked(caller, intent, ephemeralIntent, resolvedType, - aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, - callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, - options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason); + return starter.setIntent(intent).setReason(reason); } /** @@ -170,18 +148,12 @@ public class ActivityStartController { void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { mSupervisor.moveHomeStackTaskToTop(reason); - final ActivityStarter starter = createStarter(); - - mLastHomeActivityStartResult = starter.startActivityLocked(null /*caller*/, intent, - null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/, - null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/, - null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, - null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/, - 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/, - false /*componentSpecified*/, tmpOutRecord, null /*inTask*/, - "startHomeActivity: " + reason); + mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) + .setOutActivity(tmpOutRecord) + .setCallingUid(0) + .setActivityInfo(aInfo) + .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; - if (mSupervisor.inResumeTopActivity) { // If we are in resume section already, home activity will be initialized, but not // resumed (to avoid recursive resume) and will stay that way until something pokes it @@ -228,9 +200,10 @@ public class ActivityStartController { intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setComponent(new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name)); - startActivity(null, intent, null /*ephemeralIntent*/, null, ri.activityInfo, - null /*rInfo*/, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, - false, false, null, null, "startSetupActivity"); + obtainStarter(intent, "startSetupActivity") + .setCallingUid(0) + .setActivityInfo(ri.activityInfo) + .execute(); } } } @@ -246,9 +219,17 @@ public class ActivityStartController { null); // TODO: Switch to user app stacks here. - return startActivityMayWait(null, uid, callingPackage, - intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, - null, null, null, bOptions, false, userId, inTask, reason); + return obtainStarter(intent, reason) + .setCallingUid(uid) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setResultTo(resultTo) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setStartFlags(startFlags) + .setMayWait(bOptions, userId) + .setInTask(inTask) + .execute(); } final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents, @@ -262,24 +243,6 @@ public class ActivityStartController { return ret; } - /** - * TODO(b/64750076): Remove once we directly expose starter interface to callers through - * {@link #obtainStarter}. - */ - int startActivityMayWait(IApplicationThread caller, int callingUid, - String callingPackage, Intent intent, String resolvedType, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - IBinder resultTo, String resultWho, int requestCode, int startFlags, - ProfilerInfo profilerInfo, WaitResult outResult, - Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId, - TaskRecord inTask, String reason) { - return createStarter().startActivityMayWait(caller, callingUid, callingPackage, intent, - resolvedType, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, - startFlags, profilerInfo, outResult, globalConfig, bOptions, - ignoreTargetSecurity, - userId, inTask, reason); - } - int startActivities(IApplicationThread caller, int callingUid, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId, String reason) { @@ -340,11 +303,23 @@ public class ActivityStartController { ActivityOptions options = ActivityOptions.fromBundle( i == intents.length - 1 ? bOptions : null); - int res = startActivity(caller, intent, null /*ephemeralIntent*/, - resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1, - callingPid, callingUid, callingPackage, - realCallingPid, realCallingUid, 0, - options, false, componentSpecified, outActivity, null, reason); + + final int res = obtainStarter(intent, reason) + .setCaller(caller) + .setResolvedType(resolvedTypes[i]) + .setActivityInfo(aInfo) + .setResultTo(resultTo) + .setRequestCode(-1) + .setCallingPid(callingPid) + .setCallingUid(callingUid) + .setCallingPackage(callingPackage) + .setRealCallingPid(realCallingPid) + .setRealCallingUid(realCallingUid) + .setActivityOptions(options) + .setComponentSpecified(componentSpecified) + .setOutActivity(outActivity) + .execute(); + if (res < 0) { return res; } @@ -369,10 +344,11 @@ public class ActivityStartController { while (!mPendingActivityLaunches.isEmpty()) { final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); final boolean resume = doResume && mPendingActivityLaunches.isEmpty(); - final ActivityStarter starter = createStarter(); + final ActivityStarter starter = obtainStarter(null /* intent */, + "pendingActivityLaunch"); try { - starter.startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, - null, null, null /*outRecords*/); + starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, + resume, null, null, null /* outRecords */); } catch (Exception e) { Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e); pal.sendErrorResult(e.getMessage()); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 3bee4228d9fb..b12351bee413 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -74,7 +74,6 @@ import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -186,6 +185,13 @@ class ActivityStarter { // The reason we were trying to start the last activity private String mLastStartReason; + /* + * Request details provided through setter methods. Should be reset after {@link #execute()} + * to avoid unnecessarily retaining parameters. Note that the request is ignored when + * {@link #startResolvedActivity} is invoked directly. + */ + private Request mRequest = new Request(); + /** * An interface that to provide {@link ActivityStarter} instances to the controller. This is * used by tests to inject their own starter implementations for verification purposes. @@ -193,29 +199,98 @@ class ActivityStarter { @VisibleForTesting interface Factory { /** + * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}. + */ + void setController(ActivityStartController controller); + + /** * Generates an {@link ActivityStarter} that is ready to handle a new start request. * @param controller The {@link ActivityStartController} which the starter who will own * this instance. * @return an {@link ActivityStarter} */ - ActivityStarter getStarter(ActivityStartController controller, - ActivityManagerService service, ActivityStackSupervisor supervisor, - ActivityStartInterceptor interceptor); + ActivityStarter obtainStarter(); } /** * Default implementation of {@link StarterFactory}. */ static class DefaultFactory implements Factory { + private ActivityStartController mController; + private ActivityManagerService mService; + private ActivityStackSupervisor mSupervisor; + private ActivityStartInterceptor mInterceptor; + + DefaultFactory(ActivityManagerService service, + ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { + mService = service; + mSupervisor = supervisor; + mInterceptor = interceptor; + } + + @Override + public void setController(ActivityStartController controller) { + mController = controller; + } + @Override - public ActivityStarter getStarter(ActivityStartController controller, - ActivityManagerService service, ActivityStackSupervisor supervisor, - ActivityStartInterceptor interceptor) { + public ActivityStarter obtainStarter() { // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead. - return new ActivityStarter(controller, service, supervisor, interceptor); + return new ActivityStarter(mController, mService, mSupervisor, mInterceptor); } } + /** + * Container for capturing initial start request details. This information is NOT reset until + * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same + * parameters. + * + * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with + * the request object. Note that some member variables are referenced in + * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after + * execution. + */ + private static class Request { + private static final int DEFAULT_CALLING_UID = -1; + private static final int DEFAULT_CALLING_PID = 0; + + IApplicationThread caller; + Intent intent; + Intent ephemeralIntent; + String resolvedType; + ActivityInfo activityInfo; + ResolveInfo resolveInfo; + IVoiceInteractionSession voiceSession; + IVoiceInteractor voiceInteractor; + IBinder resultTo; + String resultWho; + int requestCode; + int callingPid = DEFAULT_CALLING_UID; + int callingUid = DEFAULT_CALLING_PID; + String callingPackage; + int realCallingPid; + int realCallingUid; + int startFlags; + ActivityOptions activityOptions; + boolean ignoreTargetSecurity; + boolean componentSpecified; + ActivityRecord[] outActivity; + TaskRecord inTask; + String reason; + ProfilerInfo profilerInfo; + Configuration globalConfig; + Bundle waitOptions; + int userId; + WaitResult waitResult; + + /** + * Indicates that we should wait for the result of the start request. This flag is set when + * {@link ActivityStarter#setMayWait(Bundle, int)} is called. + * {@see ActivityStarter#startActivityMayWait}. + */ + boolean mayWait; + } + ActivityStarter(ActivityStartController controller, ActivityManagerService service, ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { mController = controller; @@ -224,14 +299,44 @@ class ActivityStarter { mInterceptor = interceptor; } + ActivityRecord getStartActivity() { + return mStartActivity; + } + boolean relatedToPackage(String packageName) { return (mLastStartActivityRecord[0] != null - && packageName.equals(mLastStartActivityRecord[0].packageName)) - || (mStartActivity != null - && packageName.equals(mStartActivity.packageName)); + && packageName.equals(mLastStartActivityRecord[0].packageName)) + || (mStartActivity != null && packageName.equals(mStartActivity.packageName)); + } + + /** + * Starts an activity based on the request parameters provided earlier. + * @return The starter result. + */ + int execute() { + // TODO(b/64750076): Look into passing request directly to these methods to allow + // for transactional diffs and preprocessing. + if (mRequest.mayWait) { + return startActivityMayWait(mRequest.caller, mRequest.callingUid, + mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, + mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, + mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, + mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, + mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId, + mRequest.inTask, mRequest.reason); + } else { + return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, + mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, + mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, + mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, + mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, + mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, + mRequest.ignoreTargetSecurity, mRequest.componentSpecified, + mRequest.outActivity, mRequest.inTask, mRequest.reason); + } } - int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent, + private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, @@ -265,7 +370,6 @@ class ActivityStarter { return result != START_ABORTED ? result : START_SUCCESS; } - /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */ private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, @@ -548,8 +652,8 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); - return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, - options, inTask, outActivity); + return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, + true /* doResume */, options, inTask, outActivity); } /** @@ -610,7 +714,7 @@ class ActivityStarter { } } - final int startActivityMayWait(IApplicationThread caller, int callingUid, + private int startActivityMayWait(IApplicationThread caller, int callingUid, String callingPackage, Intent intent, String resolvedType, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, @@ -754,7 +858,7 @@ class ActivityStarter { } final ActivityRecord[] outRecord = new ActivityRecord[1]; - int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType, + int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, @@ -820,10 +924,16 @@ class ActivityStarter { } } - int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { + /** + * Starts an activity based on the provided {@link ActivityRecord} and environment parameters. + * Note that this method is called internally as well as part of {@link #startActivity}. + * + * @return The start result. + */ + int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, + ActivityRecord[] outActivity) { int result = START_CANCELED; try { mService.mWindowManager.deferSurfaceLayout(); @@ -2008,6 +2118,155 @@ class ActivityStarter { (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0; } + ActivityStarter setIntent(Intent intent) { + mRequest.intent = intent; + return this; + } + + ActivityStarter setReason(String reason) { + mRequest.reason = reason; + return this; + } + + ActivityStarter setCaller(IApplicationThread caller) { + mRequest.caller = caller; + return this; + } + + ActivityStarter setEphemeralIntent(Intent intent) { + mRequest.ephemeralIntent = intent; + return this; + } + + + ActivityStarter setResolvedType(String type) { + mRequest.resolvedType = type; + return this; + } + + ActivityStarter setActivityInfo(ActivityInfo info) { + mRequest.activityInfo = info; + return this; + } + + ActivityStarter setResolveInfo(ResolveInfo info) { + mRequest.resolveInfo = info; + return this; + } + + ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) { + mRequest.voiceSession = voiceSession; + return this; + } + + ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) { + mRequest.voiceInteractor = voiceInteractor; + return this; + } + + ActivityStarter setResultTo(IBinder resultTo) { + mRequest.resultTo = resultTo; + return this; + } + + ActivityStarter setResultWho(String resultWho) { + mRequest.resultWho = resultWho; + return this; + } + + ActivityStarter setRequestCode(int requestCode) { + mRequest.requestCode = requestCode; + return this; + } + + ActivityStarter setCallingPid(int pid) { + mRequest.callingPid = pid; + return this; + } + + ActivityStarter setCallingUid(int uid) { + mRequest.callingUid = uid; + return this; + } + + ActivityStarter setCallingPackage(String callingPackage) { + mRequest.callingPackage = callingPackage; + return this; + } + + ActivityStarter setRealCallingPid(int pid) { + mRequest.realCallingPid = pid; + return this; + } + + ActivityStarter setRealCallingUid(int uid) { + mRequest.realCallingUid = uid; + return this; + } + + ActivityStarter setStartFlags(int startFlags) { + mRequest.startFlags = startFlags; + return this; + } + + ActivityStarter setActivityOptions(ActivityOptions options) { + mRequest.activityOptions = options; + return this; + } + + ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) { + mRequest.ignoreTargetSecurity = ignoreTargetSecurity; + return this; + } + + ActivityStarter setComponentSpecified(boolean componentSpecified) { + mRequest.componentSpecified = componentSpecified; + return this; + } + + ActivityStarter setOutActivity(ActivityRecord[] outActivity) { + mRequest.outActivity = outActivity; + return this; + } + + ActivityStarter setInTask(TaskRecord inTask) { + mRequest.inTask = inTask; + return this; + } + + ActivityStarter setWaitResult(WaitResult result) { + mRequest.waitResult = result; + return this; + } + + ActivityStarter setProfilerInfo(ProfilerInfo info) { + mRequest.profilerInfo = info; + return this; + } + + ActivityStarter setGlobalConfiguration(Configuration config) { + mRequest.globalConfig = config; + return this; + } + + ActivityStarter setWaitOptions(Bundle options) { + mRequest.waitOptions = options; + return this; + } + + ActivityStarter setUserId(int userId) { + mRequest.userId = userId; + return this; + } + + ActivityStarter setMayWait(Bundle options, int userId) { + mRequest.mayWait = true; + mRequest.waitOptions = options; + mRequest.userId = userId; + + return this; + } + void dump(PrintWriter pw, String prefix) { prefix = prefix + " "; pw.print(prefix); diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java index e5872c0337cf..f821f6bdb925 100644 --- a/services/core/java/com/android/server/am/AppTaskImpl.java +++ b/services/core/java/com/android/server/am/AppTaskImpl.java @@ -122,9 +122,14 @@ class AppTaskImpl extends IAppTask.Stub { throw new IllegalArgumentException("Bad app thread " + appThread); } } - return mService.getActivityStartController().startActivityMayWait(appThread, -1, - callingPackage, intent, resolvedType, null, null, null, null, 0, 0, null, null, - null, bOptions, false, callingUser, tr, "AppTaskImpl"); + + return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl") + .setCaller(appThread) + .setCallingPackage(callingPackage) + .setResolvedType(resolvedType) + .setMayWait(bOptions, callingUser) + .setInTask(tr) + .execute(); } @Override diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 9e9318a256e3..87690d1961a9 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -935,7 +935,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - public void notePackageInstalled(String pkgName, int versionCode) { + public void notePackageInstalled(String pkgName, long versionCode) { enforceCallingPermission(); synchronized (mStats) { mStats.notePackageInstalledLocked(pkgName, versionCode); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 9d3c2ae3617a..71d6604a44bf 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -730,7 +730,7 @@ final class ProcessRecord { /* * Return true if package has been added false if not */ - public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) { + public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) { if (!pkgList.containsKey(pkg)) { ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( versionCode); diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index effb86c1bc07..5f9d61621ede 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -27,6 +27,7 @@ import android.service.procstats.ProcessStatsProto; import android.service.procstats.ProcessStatsServiceDumpProto; import android.util.ArrayMap; import android.util.AtomicFile; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -120,12 +121,12 @@ public final class ProcessStatsService extends IProcessStats.Stub { } public ProcessState getProcessStateLocked(String packageName, - int uid, int versionCode, String processName) { + int uid, long versionCode, String processName) { return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); } public ServiceState getServiceStateLocked(String packageName, int uid, - int versionCode, String processName, String className) { + long versionCode, String processName, String className) { return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, className); } @@ -150,12 +151,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { } mProcessStats.mMemFactor = memFactor; mProcessStats.mStartTime = now; - final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap + final ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pmap = mProcessStats.mPackages.getMap(); for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) { - final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg); + final SparseArray<LongSparseArray<ProcessStats.PackageState>> uids = + pmap.valueAt(ipkg); for (int iuid=uids.size()-1; iuid>=0; iuid--) { - final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid); + final LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid); for (int iver=vers.size()-1; iver>=0; iver--) { final ProcessStats.PackageState pkg = vers.valueAt(iver); final ArrayMap<String, ServiceState> services = pkg.mServices; @@ -308,17 +310,17 @@ public final class ProcessStatsService extends IProcessStats.Stub { Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); } } - ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap + ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pkgMap = stats.mPackages.getMap(); final int NPKG = pkgMap.size(); for (int ip=0; ip<NPKG; ip++) { Slog.w(TAG, "Package: " + pkgMap.keyAt(ip)); - SparseArray<SparseArray<ProcessStats.PackageState>> uids + SparseArray<LongSparseArray<ProcessStats.PackageState>> uids = pkgMap.valueAt(ip); final int NUID = uids.size(); for (int iu=0; iu<NUID; iu++) { Slog.w(TAG, " Uid: " + uids.keyAt(iu)); - SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu); + LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu); final int NVERS = vers.size(); for (int iv=0; iv<NVERS; iv++) { Slog.w(TAG, " Vers: " + vers.keyAt(iv)); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 4e3d8d27e0f9..6bd599bb0198 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -37,6 +38,7 @@ import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED; import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppGlobals; @@ -830,6 +832,9 @@ class UserController implements Handler.Callback { private IStorageManager getStorageManager() { return IStorageManager.Stub.asInterface(ServiceManager.getService("mount")); } + boolean startUser(final int userId, final boolean foreground) { + return startUser(userId, foreground, null); + } /** * Start user, if its not already running. @@ -860,7 +865,10 @@ class UserController implements Handler.Callback { * @param foreground true if user should be brought to the foreground * @return true if the user has been successfully started */ - boolean startUser(final int userId, final boolean foreground) { + boolean startUser( + final int userId, + final boolean foreground, + @Nullable IProgressListener unlockListener) { if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: switchUser() from pid=" @@ -918,6 +926,10 @@ class UserController implements Handler.Callback { final Integer userIdInt = userId; mUserLru.remove(userIdInt); mUserLru.add(userIdInt); + + if (unlockListener != null) { + uss.mUnlockProgress.addListener(unlockListener); + } } if (updateUmState) { mInjector.getUserManagerInternal().setUserState(userId, uss.state); diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index e08c65967b32..4525a4982d44 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -479,7 +479,11 @@ public class ContextHubService extends IContextHubService.Stub { retArray[i] = foundInstances.get(i).intValue(); } - Log.w(TAG, "Found " + retArray.length + " apps on hub handle " + hubHandle); + if (retArray.length == 0) { + Log.d(TAG, "No nanoapps found on hub ID " + hubHandle + " using NanoAppFilter: " + + filter); + } + return retArray; } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index c5f80bb63d2d..ba7fe7846507 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -29,8 +29,8 @@ import android.os.Binder; import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.IConditionProvider; +import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; -import android.service.notification.ZenModeConfig.ScheduleInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -45,7 +45,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.TimeZone; /** * Built-in zen condition provider for daily scheduled time-based conditions. @@ -134,7 +133,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { return; } synchronized (mSubscriptions) { - mSubscriptions.put(conditionId, toScheduleCalendar(conditionId)); + mSubscriptions.put(conditionId, ZenModeConfig.toScheduleCalendar(conditionId)); } evaluateSubscriptions(); } @@ -243,15 +242,6 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { return cal != null && cal.isInSchedule(time); } - private static ScheduleCalendar toScheduleCalendar(Uri conditionId) { - final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId); - if (schedule == null || schedule.days == null || schedule.days.length == 0) return null; - final ScheduleCalendar sc = new ScheduleCalendar(); - sc.setSchedule(schedule); - sc.setTimeZone(TimeZone.getDefault()); - return sc; - } - private void setRegistered(boolean registered) { if (mRegistered == registered) return; if (DEBUG) Slog.d(TAG, "setRegistered " + registered); diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index e5e9a378d55d..88fc65e3f7d1 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -159,7 +159,7 @@ public abstract class InstantAppResolver { long startTime) { final String packageName; final String splitName; - final int versionCode; + final long versionCode; final Intent failureIntent; if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { final AuxiliaryResolveInfo instantAppIntentInfo = @@ -241,7 +241,7 @@ public abstract class InstantAppResolver { @NonNull String instantAppPackageName, @Nullable String instantAppSplitName, @Nullable ComponentName installFailureActivity, - int versionCode, + long versionCode, @Nullable String token, boolean needsPhaseTwo) { // Construct the intent that launches the instant installer @@ -308,7 +308,8 @@ public abstract class InstantAppResolver { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName); intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); - intent.putExtra(Intent.EXTRA_VERSION_CODE, versionCode); + intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff)); + intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode); intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); if (verificationBundle != null) { intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 711352f905be..5cf08dc42a24 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -225,7 +225,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private String mPackageName; @GuardedBy("mLock") - private int mVersionCode; + private long mVersionCode; @GuardedBy("mLock") private Signature[] mSignatures; @GuardedBy("mLock") @@ -1006,7 +1006,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Use first package to define unknown values if (mPackageName == null) { mPackageName = apk.packageName; - mVersionCode = apk.versionCode; + mVersionCode = apk.getLongVersionCode(); } if (mSignatures == null) { mSignatures = apk.signatures; @@ -1057,7 +1057,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // ensure we've got appropriate package name, version code and signatures if (mPackageName == null) { mPackageName = pkgInfo.packageName; - mVersionCode = pkgInfo.versionCode; + mVersionCode = pkgInfo.getLongVersionCode(); } if (mSignatures == null) { mSignatures = pkgInfo.signatures; @@ -1149,7 +1149,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + " specified package " + params.appPackageName + " inconsistent with " + apk.packageName); } - if (mVersionCode != apk.versionCode) { + if (mVersionCode != apk.getLongVersionCode()) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " version code " + apk.versionCode + " inconsistent with " + mVersionCode); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4b6589c99df9..4e1ed9d17eb4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -111,7 +111,6 @@ import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; -import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter; import android.Manifest; import android.annotation.IntDef; @@ -187,6 +186,7 @@ import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.content.pm.VersionedPackage; +import android.content.pm.dex.IArtManager; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; @@ -242,6 +242,8 @@ import android.util.EventLog; import android.util.ExceptionUtils; import android.util.Log; import android.util.LogPrinter; +import android.util.LongSparseArray; +import android.util.LongSparseLongArray; import android.util.MathUtils; import android.util.PackageUtils; import android.util.Pair; @@ -263,16 +265,13 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IParcelFileDescriptorFactory; -import com.android.internal.os.RoSystemProperties; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -292,6 +291,7 @@ import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.pm.Settings.VersionInfo; +import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.DexLogger; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; @@ -307,30 +307,23 @@ import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.storage.DeviceStorageMonitorInternal; import dalvik.system.CloseGuard; -import dalvik.system.DexFile; import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import libcore.io.Streams; -import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedOutputStream; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -341,15 +334,12 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.Certificate; -import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -363,7 +353,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.GZIPInputStream; /** * Keep track of all those APKs everywhere. @@ -888,8 +877,8 @@ public class PackageManagerService extends IPackageManager.Stub public final @Nullable String apk; public final @NonNull SharedLibraryInfo info; - SharedLibraryEntry(String _path, String _apk, String name, int version, int type, - String declaringPackageName, int declaringPackageVersionCode) { + SharedLibraryEntry(String _path, String _apk, String name, long version, int type, + String declaringPackageName, long declaringPackageVersionCode) { path = _path; apk = _apk; info = new SharedLibraryInfo(name, version, type, new VersionedPackage( @@ -898,8 +887,8 @@ public class PackageManagerService extends IPackageManager.Stub } // Currently known shared libraries. - final ArrayMap<String, SparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>(); - final ArrayMap<String, SparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage = + final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>(); + final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage = new ArrayMap<>(); // All available activities, for your resolving pleasure. @@ -939,6 +928,8 @@ public class PackageManagerService extends IPackageManager.Stub final PackageInstallerService mInstallerService; + final ArtManagerService mArtManagerService; + private final PackageDexOptimizer mPackageDexOptimizer; // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package // is used by other apps). @@ -2661,7 +2652,7 @@ public class PackageManagerService extends IPackageManager.Stub + ps.name + "; removing system app. Last known codePath=" + ps.codePathString + ", installStatus=" + ps.installStatus + ", versionCode=" + ps.versionCode + "; scanned versionCode=" - + scannedPkg.mVersionCode); + + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); mExpectingBetter.put(ps.name, ps.codePath); } @@ -3047,6 +3038,7 @@ public class PackageManagerService extends IPackageManager.Stub } mInstallerService = new PackageInstallerService(context, this); + mArtManagerService = new ArtManagerService(this); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { @@ -3859,7 +3851,7 @@ public class PackageManagerService extends IPackageManager.Stub public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, int userId) { return getPackageInfoInternal(versionedPackage.getPackageName(), - versionedPackage.getVersionCode(), flags, Binder.getCallingUid(), userId); + versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId); } /** @@ -3868,7 +3860,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - private PackageInfo getPackageInfoInternal(String packageName, int versionCode, + private PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId, packageName); @@ -4065,7 +4057,7 @@ public class PackageManagerService extends IPackageManager.Stub if (index < 0) { continue; } - if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getVersion()) { + if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getLongVersion()) { return false; } } @@ -4461,7 +4453,8 @@ public class PackageManagerService extends IPackageManager.Stub final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - final SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i); + final LongSparseArray<SharedLibraryEntry> versionedLib + = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } @@ -4478,7 +4471,8 @@ public class PackageManagerService extends IPackageManager.Stub final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); // Resolve the package name - we use synthetic package names internally final String internalPackageName = resolveInternalPackageNameLPr( - declaringPackage.getPackageName(), declaringPackage.getVersionCode()); + declaringPackage.getPackageName(), + declaringPackage.getLongVersionCode()); final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); // Skip unused static shared libs cached less than the min period // to prevent pruning a lib needed by a subsequently installed package. @@ -4489,7 +4483,7 @@ public class PackageManagerService extends IPackageManager.Stub packagesToDelete = new ArrayList<>(); } packagesToDelete.add(new VersionedPackage(internalPackageName, - declaringPackage.getVersionCode())); + declaringPackage.getLongVersionCode())); } } } @@ -4499,7 +4493,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < packageCount; i++) { final VersionedPackage pkgToDelete = packagesToDelete.get(i); // Delete the package synchronously (will fail of the lib used for any user). - if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getVersionCode(), + if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS) == PackageManager.DELETE_SUCCEEDED) { if (volume.getUsableSpace() >= neededSpace) { @@ -4804,7 +4798,7 @@ public class PackageManagerService extends IPackageManager.Stub final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i); + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } @@ -4828,7 +4822,7 @@ public class PackageManagerService extends IPackageManager.Stub } SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(), - libInfo.getVersion(), libInfo.getType(), + libInfo.getLongVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, flags, userId)); @@ -4864,7 +4858,7 @@ public class PackageManagerService extends IPackageManager.Stub if (libIdx < 0) { continue; } - if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getVersion()) { + if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) { continue; } if (versionedPackages == null) { @@ -4945,7 +4939,7 @@ public class PackageManagerService extends IPackageManager.Stub Set<String> libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i); + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } @@ -8377,12 +8371,12 @@ public class PackageManagerService extends IPackageManager.Stub // version of the new path against what we have stored to determine // what to do. if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath); - if (pkg.mVersionCode <= ps.versionCode) { + if (pkg.getLongVersionCode() <= ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + pkg.codePath + " ignored: updated version " + ps.versionCode - + " better than this " + pkg.mVersionCode); + + " better than this " + pkg.getLongVersionCode()); if (!updatedPs.codePathString.equals(pkg.codePath)) { Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg " + ps.name + " changing from " + updatedPs.codePathString @@ -8394,7 +8388,7 @@ public class PackageManagerService extends IPackageManager.Stub updatedPs.resourcePathString = pkg.codePath; } updatedPs.pkg = pkg; - updatedPs.versionCode = pkg.mVersionCode; + updatedPs.versionCode = pkg.getLongVersionCode(); // Update the disabled system child packages to point to the package too. final int childCount = updatedPs.childPackageNames != null @@ -8405,7 +8399,7 @@ public class PackageManagerService extends IPackageManager.Stub childPackageName); if (updatedChildPkg != null) { updatedChildPkg.pkg = pkg; - updatedChildPkg.versionCode = pkg.mVersionCode; + updatedChildPkg.versionCode = pkg.getLongVersionCode(); } } } else { @@ -8423,7 +8417,7 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath + " reverting from " + ps.codePathString - + ": new version " + pkg.mVersionCode + + ": new version " + pkg.getLongVersionCode() + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), @@ -8478,7 +8472,7 @@ public class PackageManagerService extends IPackageManager.Stub throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + updatedPs.versionCode - + " better than this " + pkg.mVersionCode); + + " better than this " + pkg.getLongVersionCode()); } if (isUpdatedPkg) { @@ -8533,11 +8527,11 @@ public class PackageManagerService extends IPackageManager.Stub * already installed version, hide it. It will be scanned later * and re-added like an update. */ - if (pkg.mVersionCode <= ps.versionCode) { + if (pkg.getLongVersionCode() <= ps.versionCode) { shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + pkg.codePath - + " but new version " + pkg.mVersionCode + " better than installed " - + ps.versionCode + "; hiding system"); + + " but new version " + pkg.getLongVersionCode() + + " better than installed " + ps.versionCode + "; hiding system"); } else { /* * The newly found system app is a newer version that the @@ -8547,7 +8541,8 @@ public class PackageManagerService extends IPackageManager.Stub */ logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath + " reverting from " + ps.codePathString + ": new version " - + pkg.mVersionCode + " better than installed " + ps.versionCode); + + pkg.getLongVersionCode() + " better than installed " + + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { @@ -9125,12 +9120,12 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, int[] versions, + private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions, ArrayList<PackageParser.Package> collected, Set<String> collectedNames) { final int libNameCount = libs.size(); for (int i = 0; i < libNameCount; i++) { String libName = libs.get(i); - int version = (versions != null && versions.length == libNameCount) + long version = (versions != null && versions.length == libNameCount) ? versions[i] : PackageManager.VERSION_CODE_HIGHEST; PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version); if (libPkg != null) { @@ -9139,7 +9134,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - private PackageParser.Package findSharedNonSystemLibrary(String name, int version) { + private PackageParser.Package findSharedNonSystemLibrary(String name, long version) { synchronized (mPackages) { SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version); if (libEntry != null) { @@ -9149,8 +9144,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - private SharedLibraryEntry getSharedLibraryEntryLPr(String name, int version) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); + private SharedLibraryEntry getSharedLibraryEntryLPr(String name, long version) { + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { return null; } @@ -9158,15 +9153,15 @@ public class PackageManagerService extends IPackageManager.Stub } private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get( + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } - int previousLibVersion = -1; + long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { - final int libVersion = versionedLib.keyAt(i); + final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } @@ -9450,14 +9445,14 @@ public class PackageManagerService extends IPackageManager.Stub } private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries, - @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests, + @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, @Nullable PackageParser.Package changingLib, boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries) throws PackageManagerException { final int libCount = requestedLibraries.size(); for (int i = 0; i < libCount; i++) { final String libName = requestedLibraries.get(i); - final int libVersion = requiredVersions != null ? requiredVersions[i] + final long libVersion = requiredVersions != null ? requiredVersions[i] : SharedLibraryInfo.VERSION_UNDEFINED; final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion); if (libEntry == null) { @@ -9472,11 +9467,11 @@ public class PackageManagerService extends IPackageManager.Stub } } else { if (requiredVersions != null && requiredCertDigests != null) { - if (libEntry.info.getVersion() != requiredVersions[i]) { + if (libEntry.info.getLongVersion() != requiredVersions[i]) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + packageName + " requires unavailable static shared" + " library " + libName + " version " - + libEntry.info.getVersion() + "; failing!"); + + libEntry.info.getLongVersion() + "; failing!"); } PackageParser.Package libPkg = mPackages.get(libEntry.apk); @@ -9500,7 +9495,7 @@ public class PackageManagerService extends IPackageManager.Stub if (expectedCertDigests.length != libCertDigests.length) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + packageName + " requires differently signed" + - " static sDexLoadReporter.java:45.19hared library; failing!"); + " static shared library; failing!"); } // Use a predictable order as signature order may vary @@ -9835,7 +9830,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage, disabledPkgSetting, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi, - pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode, + pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(), pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user, true /*allowInstall*/, instantApp, virtualPreload, parentPackageName, pkg.getChildPackageNames(), @@ -10403,20 +10398,20 @@ public class PackageManagerService extends IPackageManager.Stub } // The version codes must be ordered as lib versions - int minVersionCode = Integer.MIN_VALUE; - int maxVersionCode = Integer.MAX_VALUE; + long minVersionCode = Long.MIN_VALUE; + long maxVersionCode = Long.MAX_VALUE; - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get( + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib != null) { final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { SharedLibraryInfo libInfo = versionedLib.valueAt(i).info; - final int libVersionCode = libInfo.getDeclaringPackage() - .getVersionCode(); - if (libInfo.getVersion() < pkg.staticSharedLibVersion) { + final long libVersionCode = libInfo.getDeclaringPackage() + .getLongVersionCode(); + if (libInfo.getLongVersion() < pkg.staticSharedLibVersion) { minVersionCode = Math.max(minVersionCode, libVersionCode + 1); - } else if (libInfo.getVersion() > pkg.staticSharedLibVersion) { + } else if (libInfo.getLongVersion() > pkg.staticSharedLibVersion) { maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1); } else { minVersionCode = maxVersionCode = libVersionCode; @@ -10424,7 +10419,8 @@ public class PackageManagerService extends IPackageManager.Stub } } } - if (pkg.mVersionCode < minVersionCode || pkg.mVersionCode > maxVersionCode) { + if (pkg.getLongVersionCode() < minVersionCode + || pkg.getLongVersionCode() > maxVersionCode) { throw new PackageManagerException("Static shared" + " lib version codes must be ordered as lib versions"); } @@ -10514,11 +10510,11 @@ public class PackageManagerService extends IPackageManager.Stub } } - private boolean addSharedLibraryLPw(String path, String apk, String name, int version, - int type, String declaringPackageName, int declaringVersionCode) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); + private boolean addSharedLibraryLPw(String path, String apk, String name, long version, + int type, String declaringPackageName, long declaringVersionCode) { + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { - versionedLib = new SparseArray<>(); + versionedLib = new LongSparseArray<>(); mSharedLibraries.put(name, versionedLib); if (type == SharedLibraryInfo.TYPE_STATIC) { mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib); @@ -10532,8 +10528,8 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - private boolean removeSharedLibraryLPw(String name, int version) { - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); + private boolean removeSharedLibraryLPw(String name, long version) { + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { return false; } @@ -10572,6 +10568,7 @@ public class PackageManagerService extends IPackageManager.Stub // Set up information for our fall-back user intent resolution activity. mPlatformPackage = pkg; pkg.mVersionCode = mSdkVersion; + pkg.mVersionCodeMajor = 0; mAndroidApplication = pkg.applicationInfo; if (!mResolverReplaced) { mResolveActivity.applicationInfo = mAndroidApplication; @@ -10613,7 +10610,7 @@ public class PackageManagerService extends IPackageManager.Stub // names to allow install of multiple versions, so use name from manifest. if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName, pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC, - pkg.manifestPackageName, pkg.mVersionCode)) { + pkg.manifestPackageName, pkg.getLongVersionCode())) { hasStaticSharedLibs = true; } else { Slog.w(TAG, "Package " + pkg.packageName + " library " @@ -10659,7 +10656,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!addSharedLibraryLPw(null, pkg.packageName, name, SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_DYNAMIC, - pkg.packageName, pkg.mVersionCode)) { + pkg.packageName, pkg.getLongVersionCode())) { Slog.w(TAG, "Package " + pkg.packageName + " library " + name + " already exists; skipping"); } @@ -14654,6 +14651,9 @@ public class PackageManagerService extends IPackageManager.Stub verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode); + verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE, + pkgLite.getLongVersionCode()); + if (verificationInfo != null) { if (verificationInfo.originatingUri != null) { verification.putExtra(Intent.EXTRA_ORIGINATING_URI, @@ -16584,7 +16584,8 @@ public class PackageManagerService extends IPackageManager.Stub // unless this is the exact same version code which is useful for // development. PackageParser.Package existingPkg = mPackages.get(pkg.packageName); - if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) { + if (existingPkg != null && + existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) { res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring " + "static-shared libs cannot be updated"); return; @@ -16891,17 +16892,16 @@ public class PackageManagerService extends IPackageManager.Stub final boolean canViewInstantApps = canViewInstantApps(callingUid, userId); Preconditions.checkNotNull(versionedPackage); Preconditions.checkNotNull(observer); - Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(), + Preconditions.checkArgumentInRange(versionedPackage.getLongVersionCode(), PackageManager.VERSION_CODE_HIGHEST, - Integer.MAX_VALUE, "versionCode must be >= -1"); + Long.MAX_VALUE, "versionCode must be >= -1"); final String packageName = versionedPackage.getPackageName(); - final int versionCode = versionedPackage.getVersionCode(); + final long versionCode = versionedPackage.getLongVersionCode(); final String internalPackageName; synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs - internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(), - versionedPackage.getVersionCode()); + internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode); } final int uid = Binder.getCallingUid(); @@ -17009,24 +17009,24 @@ public class PackageManagerService extends IPackageManager.Stub return pkg.packageName; } - private String resolveInternalPackageNameLPr(String packageName, int versionCode) { + private String resolveInternalPackageNameLPr(String packageName, long versionCode) { // Handle renamed packages String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); packageName = normalizedPackageName != null ? normalizedPackageName : packageName; // Is this a static library? - SparseArray<SharedLibraryEntry> versionedLib = + LongSparseArray<SharedLibraryEntry> versionedLib = mStaticLibsByDeclaringPackage.get(packageName); if (versionedLib == null || versionedLib.size() <= 0) { return packageName; } // Figure out which lib versions the caller can see - SparseIntArray versionsCallerCanSee = null; + LongSparseLongArray versionsCallerCanSee = null; final int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID && callingAppId != Process.ROOT_UID) { - versionsCallerCanSee = new SparseIntArray(); + versionsCallerCanSee = new LongSparseLongArray(); String libName = versionedLib.valueAt(0).info.getName(); String[] uidPackages = getPackagesForUid(Binder.getCallingUid()); if (uidPackages != null) { @@ -17034,7 +17034,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageSetting ps = mSettings.getPackageLPr(uidPackage); final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName); if (libIdx >= 0) { - final int libVersion = ps.usesStaticLibrariesVersions[libIdx]; + final long libVersion = ps.usesStaticLibrariesVersions[libIdx]; versionsCallerCanSee.append(libVersion, libVersion); } } @@ -17052,10 +17052,10 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < versionCount; i++) { SharedLibraryEntry libEntry = versionedLib.valueAt(i); if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey( - libEntry.info.getVersion()) < 0) { + libEntry.info.getLongVersion()) < 0) { continue; } - final int libVersionCode = libEntry.info.getDeclaringPackage().getVersionCode(); + final long libVersionCode = libEntry.info.getDeclaringPackage().getLongVersionCode(); if (versionCode != PackageManager.VERSION_CODE_HIGHEST) { if (libVersionCode == versionCode) { return libEntry.apk; @@ -17063,7 +17063,7 @@ public class PackageManagerService extends IPackageManager.Stub } else if (highestVersion == null) { highestVersion = libEntry; } else if (libVersionCode > highestVersion.info - .getDeclaringPackage().getVersionCode()) { + .getDeclaringPackage().getLongVersionCode()) { highestVersion = libEntry; } } @@ -17192,7 +17192,7 @@ public class PackageManagerService extends IPackageManager.Stub * persisting settings for later use * sending a broadcast if necessary */ - int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) { + int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) { final PackageRemovedInfo info = new PackageRemovedInfo(this); final boolean res; @@ -17243,7 +17243,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!ArrayUtils.isEmpty(libClientPackages)) { Slog.w(TAG, "Not removing package " + pkg.manifestPackageName + " hosting lib " + libEntry.info.getName() + " version " - + libEntry.info.getVersion() + " used by " + libClientPackages + + libEntry.info.getLongVersion() + " used by " + libClientPackages + " for user " + currUserId); return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY; } @@ -20527,7 +20527,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); final Iterator<String> it = mSharedLibraries.keySet().iterator(); while (it.hasNext()) { String libName = it.next(); - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName); + LongSparseArray<SharedLibraryEntry> versionedLib + = mSharedLibraries.get(libName); if (versionedLib == null) { continue; } @@ -20547,7 +20548,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } pw.print(libEntry.info.getName()); if (libEntry.info.isStatic()) { - pw.print(" version=" + libEntry.info.getVersion()); + pw.print(" version=" + libEntry.info.getLongVersion()); } if (!checkin) { pw.print(" -> "); @@ -20916,7 +20917,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); final int count = mSharedLibraries.size(); for (int i = 0; i < count; i++) { final String libName = mSharedLibraries.keyAt(i); - SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName); + LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName); if (versionedLib == null) { continue; } @@ -22273,6 +22274,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return mInstallerService; } + @Override + public IArtManager getArtManager() { + return mArtManagerService; + } + private boolean userNeedsBadging(int userId) { int index = mUserNeedsBadging.indexOfKey(userId); if (index < 0) { @@ -22421,11 +22427,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); */ private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after) throws PackageManagerException { - if (after.versionCode < before.mVersionCode) { + if (after.getLongVersionCode() < before.getLongVersionCode()) { throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, "Update version code " + after.versionCode + " is older than current " - + before.mVersionCode); - } else if (after.versionCode == before.mVersionCode) { + + before.getLongVersionCode()); + } else if (after.getLongVersionCode() == before.getLongVersionCode()) { if (after.baseRevisionCode < before.baseRevisionCode) { throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, "Update base revision code " + after.baseRevisionCode @@ -22612,12 +22618,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public int getVersionCodeForPackage(String packageName) throws RemoteException { + public long getVersionCodeForPackage(String packageName) throws RemoteException { try { int callingUser = UserHandle.getUserId(Binder.getCallingUid()); PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser); if (pInfo != null) { - return pInfo.versionCode; + return pInfo.getLongVersionCode(); } } catch (Exception e) { } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 620044423f38..a7cced75bfd6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1245,7 +1245,7 @@ class PackageManagerShellCommand extends ShellCommand { final PrintWriter pw = getOutPrintWriter(); int flags = 0; int userId = UserHandle.USER_ALL; - int versionCode = PackageManager.VERSION_CODE_HIGHEST; + long versionCode = PackageManager.VERSION_CODE_HIGHEST; String opt; while ((opt = getNextOption()) != null) { @@ -1257,7 +1257,7 @@ class PackageManagerShellCommand extends ShellCommand { userId = UserHandle.parseUserArg(getNextArgRequired()); break; case "--versionCode": - versionCode = Integer.parseInt(getNextArgRequired()); + versionCode = Long.parseLong(getNextArgRequired()); break; default: pw.println("Error: Unknown option: " + opt); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 258dd4d4dcb3..2b91b7d38b4f 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -50,9 +50,9 @@ public final class PackageSetting extends PackageSettingBase { PackageSetting(String name, String realName, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, - int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName, + long pVersionCode, int pkgFlags, int privateFlags, String parentPackageName, List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries, - int[] usesStaticLibrariesVersions) { + long[] usesStaticLibrariesVersions) { super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames, diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index a83876848910..9733624cddc6 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -75,7 +75,7 @@ public abstract class PackageSettingBase extends SettingBase { String resourcePathString; String[] usesStaticLibraries; - int[] usesStaticLibrariesVersions; + long[] usesStaticLibrariesVersions; /** * The path under which native libraries have been unpacked. This path is @@ -105,7 +105,7 @@ public abstract class PackageSettingBase extends SettingBase { long timeStamp; long firstInstallTime; long lastUpdateTime; - int versionCode; + long versionCode; boolean uidError; @@ -149,9 +149,9 @@ public abstract class PackageSettingBase extends SettingBase { PackageSettingBase(String name, String realName, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, - int pVersionCode, int pkgFlags, int pkgPrivateFlags, + long pVersionCode, int pkgFlags, int pkgPrivateFlags, String parentPackageName, List<String> childPackageNames, - String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) { + String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) { super(pkgFlags, pkgPrivateFlags); this.name = name; this.realName = realName; @@ -180,7 +180,7 @@ public abstract class PackageSettingBase extends SettingBase { void init(File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, - String cpuAbiOverrideString, int pVersionCode) { + String cpuAbiOverrideString, long pVersionCode) { this.codePath = codePath; this.codePathString = codePath.toString(); this.resourcePath = resourcePath; diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index f0ce3c9d230e..388491642e1e 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -60,10 +60,8 @@ public final class SELinuxMMAC { // to synchronize access during policy load and access attempts. private static List<Policy> sPolicies = new ArrayList<>(); - /** Path to MAC permissions on system image */ - private static final File[] MAC_PERMISSIONS = - { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"), - new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") }; + // Required MAC permissions files. + private static List<File> sMacPermissions = new ArrayList<>(); // Append privapp to existing seinfo label private static final String PRIVILEGED_APP_STR = ":privapp"; @@ -76,11 +74,11 @@ public final class SELinuxMMAC { /** * Load the mac_permissions.xml file containing all seinfo assignments used to - * label apps. The loaded mac_permissions.xml file is determined by the - * MAC_PERMISSIONS class variable which is set at class load time which itself - * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on + * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and + * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively. + * odm_mac_permissions.xml on /odm partition is optional. For further guidance on * the proper structure of a mac_permissions.xml file consult the source code - * located at system/sepolicy/mac_permissions.xml. + * located at system/sepolicy/private/mac_permissions.xml. * * @return boolean indicating if policy was correctly loaded. A value of false * typically indicates a structural problem with the xml or incorrectly @@ -93,10 +91,42 @@ public final class SELinuxMMAC { FileReader policyFile = null; XmlPullParser parser = Xml.newPullParser(); - for (int i = 0; i < MAC_PERMISSIONS.length; i++) { + + synchronized (sMacPermissions) { + // Only initialize it once. + if (sMacPermissions.isEmpty()) { + // Platform mac permissions. + sMacPermissions.add(new File( + Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml")); + + // Vendor mac permissions. + // The filename has been renamed from nonplat_mac_permissions to + // vendor_mac_permissions. Either of them should exist. + File vendorMacPermission = new File( + Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml"); + if (vendorMacPermission.exists()) { + sMacPermissions.add(vendorMacPermission); + } else { + // For backward compatibility. + sMacPermissions.add(new File(Environment.getVendorDirectory(), + "/etc/selinux/nonplat_mac_permissions.xml")); + } + + // ODM mac permissions (optional). + File odmMacPermission = new File( + Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml"); + if (odmMacPermission.exists()) { + sMacPermissions.add(odmMacPermission); + } + } + } + + final int count = sMacPermissions.size(); + for (int i = 0; i < count; ++i) { + File macPermission = sMacPermissions.get(i); try { - policyFile = new FileReader(MAC_PERMISSIONS[i]); - Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]); + policyFile = new FileReader(macPermission); + Slog.d(TAG, "Using policy file " + macPermission); parser.setInput(policyFile); parser.nextTag(); @@ -120,13 +150,13 @@ public final class SELinuxMMAC { StringBuilder sb = new StringBuilder("Exception @"); sb.append(parser.getPositionDescription()); sb.append(" while parsing "); - sb.append(MAC_PERMISSIONS[i]); + sb.append(macPermission); sb.append(":"); sb.append(ex); Slog.w(TAG, sb.toString()); return false; } catch (IOException ioe) { - Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe); + Slog.w(TAG, "Exception parsing " + macPermission, ioe); return false; } finally { IoUtils.closeQuietly(policyFile); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index af1a4d148856..4a5772fba96f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -593,10 +593,10 @@ public final class Settings { PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, - String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, int + String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int pkgFlags, int pkgPrivateFlags, String parentPackageName, List<String> childPackageNames, String[] usesStaticLibraries, - int[] usesStaticLibraryNames) { + long[] usesStaticLibraryNames) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.appId == uid) { @@ -673,11 +673,11 @@ public final class Settings { static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg, PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser, File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi, - String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags, + String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, String parentPkgName, List<String> childPkgNames, UserManagerService userManager, - String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) { + String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) { final PackageSetting pkgSetting; if (originalPkg != null) { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " @@ -788,7 +788,7 @@ public final class Settings { @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames, @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries, - @Nullable int[] usesStaticLibrariesVersions) throws PackageManagerException { + @Nullable long[] usesStaticLibrariesVersions) throws PackageManagerException { final String pkgName = pkgSetting.name; if (pkgSetting.sharedUser != sharedUser) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -951,8 +951,8 @@ public final class Settings { p.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; p.cpuAbiOverrideString = pkg.cpuAbiOverride; // Update version code if needed - if (pkg.mVersionCode != p.versionCode) { - p.versionCode = pkg.mVersionCode; + if (pkg.getLongVersionCode() != p.versionCode) { + p.versionCode = pkg.getLongVersionCode(); } // Update signatures if needed. if (p.signatures.mSignatures == null) { @@ -2289,9 +2289,9 @@ public final class Settings { String libName = parser.getAttributeValue(null, ATTR_NAME); String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION); - int libVersion = -1; + long libVersion = -1; try { - libVersion = Integer.parseInt(libVersionStr); + libVersion = Long.parseLong(libVersionStr); } catch (NumberFormatException e) { // ignore } @@ -2299,7 +2299,7 @@ public final class Settings { if (libName != null && libVersion >= 0) { outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class, outPs.usesStaticLibraries, libName); - outPs.usesStaticLibrariesVersions = ArrayUtils.appendInt( + outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong( outPs.usesStaticLibrariesVersions, libVersion); } @@ -2308,7 +2308,7 @@ public final class Settings { } void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries, - int[] usesStaticLibraryVersions) throws IOException { + long[] usesStaticLibraryVersions) throws IOException { if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions) || usesStaticLibraries.length != usesStaticLibraryVersions.length) { return; @@ -2316,10 +2316,10 @@ public final class Settings { final int libCount = usesStaticLibraries.length; for (int i = 0; i < libCount; i++) { final String libName = usesStaticLibraries[i]; - final int libVersion = usesStaticLibraryVersions[i]; + final long libVersion = usesStaticLibraryVersions[i]; serializer.startTag(null, TAG_USES_STATIC_LIB); serializer.attribute(null, ATTR_NAME, libName); - serializer.attribute(null, ATTR_VERSION, Integer.toString(libVersion)); + serializer.attribute(null, ATTR_VERSION, Long.toString(libVersion)); serializer.endTag(null, TAG_USES_STATIC_LIB); } } @@ -3563,10 +3563,10 @@ public final class Settings { resourcePathStr = codePathStr; } String version = parser.getAttributeValue(null, "version"); - int versionCode = 0; + long versionCode = 0; if (version != null) { try { - versionCode = Integer.parseInt(version); + versionCode = Long.parseLong(version); } catch (NumberFormatException e) { } } @@ -3679,7 +3679,7 @@ public final class Settings { long lastUpdateTime = 0; PackageSetting packageSetting = null; String version = null; - int versionCode = 0; + long versionCode = 0; String parentPackageName; try { name = parser.getAttributeValue(null, ATTR_NAME); @@ -3707,7 +3707,7 @@ public final class Settings { version = parser.getAttributeValue(null, "version"); if (version != null) { try { - versionCode = Integer.parseInt(version); + versionCode = Long.parseLong(version); } catch (NumberFormatException e) { } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index ba97c428bb44..7bab318069ff 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -725,7 +725,7 @@ class ShortcutPackage extends ShortcutPackageItem { // This means if a system app's version code doesn't change on an OTA, // we don't notice it's updated. But that's fine since their version code *should* // really change on OTAs. - if ((getPackageInfo().getVersionCode() == pi.versionCode) + if ((getPackageInfo().getVersionCode() == pi.getLongVersionCode()) && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime) && areAllActivitiesStillEnabled()) { return false; @@ -759,11 +759,11 @@ class ShortcutPackage extends ShortcutPackageItem { if (ShortcutService.DEBUG) { Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(), (isNewApp ? "added" : "updated"), - getPackageInfo().getVersionCode(), pi.versionCode)); + getPackageInfo().getVersionCode(), pi.getLongVersionCode())); } getPackageInfo().updateFromPackageInfo(pi); - final int newVersionCode = getPackageInfo().getVersionCode(); + final long newVersionCode = getPackageInfo().getVersionCode(); // See if there are any shortcuts that were prevented restoring because the app was of a // lower version, and re-enable them. @@ -1412,7 +1412,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_FLAGS, flags); // Set the publisher version code at every backup. - final int packageVersionCode = getPackageInfo().getVersionCode(); + final long packageVersionCode = getPackageInfo().getVersionCode(); if (packageVersionCode == 0) { s.wtf("Package version code should be available at this point."); // However, 0 is a valid version code, so we just go ahead with it... diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java index 3a9bbc8926ed..b14e9c99b138 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java @@ -59,8 +59,8 @@ class ShortcutPackageInfo { * been installed yet. */ private boolean mIsShadow; - private int mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; - private int mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; + private long mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; + private long mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; private long mLastUpdateTime; private ArrayList<byte[]> mSigHashes; @@ -73,7 +73,7 @@ class ShortcutPackageInfo { private boolean mBackupAllowed; private boolean mBackupSourceBackupAllowed; - private ShortcutPackageInfo(int versionCode, long lastUpdateTime, + private ShortcutPackageInfo(long versionCode, long lastUpdateTime, ArrayList<byte[]> sigHashes, boolean isShadow) { mVersionCode = versionCode; mLastUpdateTime = lastUpdateTime; @@ -96,11 +96,11 @@ class ShortcutPackageInfo { mIsShadow = shadow; } - public int getVersionCode() { + public long getVersionCode() { return mVersionCode; } - public int getBackupSourceVersionCode() { + public long getBackupSourceVersionCode() { return mBackupSourceVersionCode; } @@ -123,7 +123,7 @@ class ShortcutPackageInfo { */ public void updateFromPackageInfo(@NonNull PackageInfo pi) { if (pi != null) { - mVersionCode = pi.versionCode; + mVersionCode = pi.getLongVersionCode(); mLastUpdateTime = pi.lastUpdateTime; mBackupAllowed = ShortcutService.shouldBackupApp(pi); mBackupAllowedInitialized = true; @@ -145,7 +145,7 @@ class ShortcutPackageInfo { Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup"); return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED; } - if (!anyVersionOkay && (currentPackage.versionCode < mBackupSourceVersionCode)) { + if (!anyVersionOkay && (currentPackage.getLongVersionCode() < mBackupSourceVersionCode)) { Slog.w(TAG, String.format( "Can't restore: package current version %d < backed up version %d", currentPackage.versionCode, mBackupSourceVersionCode)); @@ -162,11 +162,12 @@ class ShortcutPackageInfo { Slog.e(TAG, "Can't get signatures: package=" + packageName); return null; } - final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime, - BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false); + final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(), + pi.lastUpdateTime, BackupUtils.hashSignatureArray(pi.signatures), + /* shadow=*/ false); ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi); - ret.mBackupSourceVersionCode = pi.versionCode; + ret.mBackupSourceVersionCode = pi.getLongVersionCode(); return ret; } @@ -213,7 +214,7 @@ class ShortcutPackageInfo { throws IOException, XmlPullParserException { // Don't use the version code from the backup file. - final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION, + final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN); final long lastUpdateTime = ShortcutService.parseLongAttribute( @@ -225,7 +226,7 @@ class ShortcutPackageInfo { // We didn't used to save these attributes, and all backed up shortcuts were from // apps that support backups, so the default values take this fact into consideration. - final int backupSourceVersion = ShortcutService.parseIntAttribute(parser, + final long backupSourceVersion = ShortcutService.parseLongAttribute(parser, ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN); // Note the only time these "true" default value is used is when restoring from an old diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index 689099cfcc3a..0629d9e45d64 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -108,7 +108,7 @@ abstract class ShortcutPackageItem { return; // Not installed, no need to restore yet. } int restoreBlockReason; - int currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; + long currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN; if (!mPackageInfo.hasSignatures()) { s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId @@ -116,7 +116,7 @@ abstract class ShortcutPackageItem { restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH; } else { final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId); - currentVersionCode = pi.versionCode; + currentVersionCode = pi.getLongVersionCode(); restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion()); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index dbf413f0415d..dc481ca11c7b 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -27,7 +27,6 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; -import android.app.AppGlobals; import android.app.IActivityManager; import android.app.IStopUserCallback; import android.app.KeyguardManager; @@ -50,6 +49,7 @@ import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; +import android.os.IProgressListener; import android.os.IUserManager; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -71,10 +71,6 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; -import android.text.TextUtils; import android.util.AtomicFile; import android.util.IntArray; import android.util.Log; @@ -113,9 +109,9 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -378,22 +374,45 @@ public class UserManagerService extends IUserManager.Stub { private final BroadcastReceiver mDisableQuietModeCallback = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) { - final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT); - final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, 0); - setQuietModeEnabled(userHandle, false); - if (target != null) { - try { - mContext.startIntentSender(target, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - /* ignore */ - } - } + if (!ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) { + return; } + final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT); + final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); + setQuietModeEnabled(userHandle, false, target); } }; /** + * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. + * + * @see {@link #trySetQuietModeDisabled(int, IntentSender)} + */ + private class DisableQuietModeUserUnlockedCallback extends IProgressListener.Stub { + private final IntentSender mTarget; + + public DisableQuietModeUserUnlockedCallback(IntentSender target) { + Preconditions.checkNotNull(target); + mTarget = target; + } + + @Override + public void onStarted(int id, Bundle extras) {} + + @Override + public void onProgress(int id, int progress, Bundle extras) {} + + @Override + public void onFinished(int id, Bundle extras) { + try { + mContext.startIntentSender(mTarget, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + Slog.e(LOG_TAG, "Failed to start the target in the callback", e); + } + } + } + + /** * Whether all users should be created ephemeral. */ @GuardedBy("mUsersLock") @@ -765,7 +784,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setQuietModeEnabled(int userHandle, boolean enableQuietMode) { + public void setQuietModeEnabled(int userHandle, boolean enableQuietMode, IntentSender target) { checkManageUsersPermission("silence profile"); boolean changed = false; UserInfo profile, parent; @@ -792,7 +811,11 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.getService(ActivityManagerInternal.class) .killForegroundAppsForUser(userHandle); } else { - ActivityManager.getService().startUserInBackground(userHandle); + IProgressListener callback = target != null + ? new DisableQuietModeUserUnlockedCallback(target) + : null; + ActivityManager.getService().startUserInBackgroundWithListener( + userHandle, callback); } } catch (RemoteException e) { Slog.e(LOG_TAG, "fail to start/stop user for quiet mode", e); @@ -820,12 +843,13 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) { + public boolean trySetQuietModeDisabled( + @UserIdInt int userHandle, @Nullable IntentSender target) { checkManageUsersPermission("silence profile"); if (StorageManager.isUserKeyUnlocked(userHandle) || !mLockPatternUtils.isSecure(userHandle)) { // if the user is already unlocked, no need to show a profile challenge - setQuietModeEnabled(userHandle, false); + setQuietModeEnabled(userHandle, false, target); return true; } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java new file mode 100644 index 000000000000..5a1f840e945b --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -0,0 +1,155 @@ +/* + * 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.pm.dex; + +import android.Manifest; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.dex.ArtManager; +import android.os.Binder; +import android.os.Handler; +import android.os.RemoteException; +import android.content.pm.IPackageManager; +import android.content.pm.dex.ISnapshotRuntimeProfileCallback; +import android.os.SystemProperties; +import android.util.Slog; + +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.Preconditions; + +/** + * A system service that provides access to runtime and compiler artifacts. + * + * This service is not accessed by users directly, instead one uses an instance of + * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows: + * <p/> + * {@code context().getPackageManager().getArtManager();} + * <p class="note"> + * Note: Accessing runtime artifacts may require extra permissions. For example querying the + * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES} + * which is a system-level permission that will not be granted to normal apps. + */ +public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { + private static final String TAG = "ArtManagerService"; + + private static boolean DEBUG = false; + private static boolean DEBUG_IGNORE_PERMISSIONS = false; + + private final IPackageManager mPackageManager; + private final Handler mHandler; + + public ArtManagerService(IPackageManager pm) { + mPackageManager = pm; + mHandler = new Handler(BackgroundThread.getHandler().getLooper()); + } + + @Override + public void snapshotRuntimeProfile(String packageName, String codePath, + ISnapshotRuntimeProfileCallback callback) { + // Sanity checks on the arguments. + Preconditions.checkStringNotEmpty(packageName); + Preconditions.checkStringNotEmpty(codePath); + Preconditions.checkNotNull(callback); + + // Verify that the caller has the right permissions. + checkReadRuntimeProfilePermission(); + + if (DEBUG) { + Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); + } + + PackageInfo info = null; + try { + // Note that we use the default user 0 to retrieve the package info. + // This doesn't really matter because for user 0 we always get a package back (even if + // it's not installed for the user 0). It is ok because we only care about the code + // paths and not if the package is enabled or not for the user. + + // TODO(calin): consider adding an API to PMS which can retrieve the + // PackageParser.Package. + info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0); + } catch (RemoteException ignored) { + // Should not happen. + } + if (info == null) { + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); + return; + } + + boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath); + String[] splitCodePaths = info.applicationInfo.getSplitCodePaths(); + if (!pathFound && (splitCodePaths != null)) { + for (String path : splitCodePaths) { + if (path.equals(codePath)) { + pathFound = true; + break; + } + } + } + if (!pathFound) { + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND); + return; + } + + // All good, move forward and get the profile. + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + } + + @Override + public boolean isRuntimeProfilingEnabled() { + // Verify that the caller has the right permissions. + checkReadRuntimeProfilePermission(); + + return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + } + + /** + * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message + * on the internal {@code mHandler}. + */ + private void postError(ISnapshotRuntimeProfileCallback callback, String packageName, + int errCode) { + mHandler.post(() -> { + try { + callback.onError(errCode); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e); + } + }); + } + + /** + * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}. + * If not, it throws a {@link SecurityException}. + */ + private void checkReadRuntimeProfilePermission() { + if (DEBUG_IGNORE_PERMISSIONS) { + return; + } + try { + int result = mPackageManager.checkUidPermission( + Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid()); + if (result != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need " + + Manifest.permission.READ_RUNTIME_PROFILES + + " permission to snapshot profiles."); + } + } catch (RemoteException e) { + // Should not happen. + } + } +} diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java index 38101926c0d0..8a40b4dd49fb 100644 --- a/services/core/java/com/android/server/power/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java @@ -49,11 +49,21 @@ public class BatterySaverPolicy extends ContentObserver { public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE. - // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. + /** Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. */ public static final int GPS_MODE_NO_CHANGE = 0; - // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode - // is enabled and the screen is off. + + /** + * Value of batterySaverGpsMode such that GPS is disabled when battery saver mode + * is enabled and the screen is off. + */ public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1; + + /** + * Value of batterySaverGpsMode such that location should be disabled altogether + * when battery saver mode is enabled and the screen is off. + */ + public static final int GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2; + // Secure setting for GPS behavior when battery saver mode is on. public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode"; @@ -344,7 +354,7 @@ public class BatterySaverPolicy extends ContentObserver { // Get default value from Settings.Secure final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE, - GPS_MODE_NO_CHANGE); + GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode); // Non-device-specific parameters. @@ -446,6 +456,12 @@ public class BatterySaverPolicy extends ContentObserver { } } + public int getGpsMode() { + synchronized (mLock) { + return mGpsMode; + } + } + public ArrayMap<String, String> getFileValues(boolean interactive) { synchronized (mLock) { return interactive ? mFilesForInteractive : mFilesForNoninteractive; diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index a6bca0bf6c4c..d4627c2da07e 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -84,6 +84,23 @@ public class BatterySaverController implements BatterySaverPolicyListener { */ private boolean mPreviouslyEnabled; + @GuardedBy("mLock") + private boolean mIsInteractive; + + /** + * Read-only list of plugins. No need for synchronization. + */ + private final Plugin[] mPlugins; + + /** + * Plugin interface. All methods are guaranteed to be called on the same (handler) thread. + */ + public interface Plugin { + void onSystemReady(BatterySaverController caller); + + void onBatterySaverChanged(BatterySaverController caller); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -109,6 +126,12 @@ public class BatterySaverController implements BatterySaverPolicyListener { mBatterySaverPolicy = policy; mBatterySaverPolicy.addListener(this); mFileUpdater = new FileUpdater(context); + + // Initialize plugins. + final ArrayList<Plugin> plugins = new ArrayList<>(); + plugins.add(new BatterySaverLocationPlugin(mContext)); + + mPlugins = plugins.toArray(new Plugin[plugins.size()]); } /** @@ -121,7 +144,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { } /** - * Called by {@link PowerManagerService} on system ready.. + * Called by {@link PowerManagerService} on system ready. */ public void systemReady() { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); @@ -130,6 +153,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class) .isRuntimeRestarted()); + mHandler.postSystemReady(); } private PowerManager getPowerManager() { @@ -154,6 +178,8 @@ public class BatterySaverController implements BatterySaverPolicyListener { private static final int ARG_DONT_SEND_BROADCAST = 0; private static final int ARG_SEND_BROADCAST = 1; + private static final int MSG_SYSTEM_READY = 2; + public MyHandler(Looper looper) { super(looper); } @@ -163,12 +189,22 @@ public class BatterySaverController implements BatterySaverPolicyListener { ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget(); } + public void postSystemReady() { + obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget(); + } + @Override public void dispatchMessage(Message msg) { switch (msg.what) { case MSG_STATE_CHANGED: handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST); break; + + case MSG_SYSTEM_READY: + for (Plugin p : mPlugins) { + p.onSystemReady(BatterySaverController.this); + } + break; } } } @@ -188,12 +224,24 @@ public class BatterySaverController implements BatterySaverPolicyListener { } /** @return whether battery saver is enabled or not. */ - boolean isEnabled() { + public boolean isEnabled() { synchronized (mLock) { return mEnabled; } } + /** @return whether device is in interactive state. */ + public boolean isInteractive() { + synchronized (mLock) { + return mIsInteractive; + } + } + + /** @return Battery saver policy. */ + public BatterySaverPolicy getBatterySaverPolicy() { + return mBatterySaverPolicy; + } + /** * @return true if launch boost should currently be disabled. */ @@ -230,6 +278,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]); enabled = mEnabled; + mIsInteractive = isInteractive; if (enabled) { @@ -250,6 +299,10 @@ public class BatterySaverController implements BatterySaverPolicyListener { mFileUpdater.writeFiles(fileValues); } + for (Plugin p : mPlugins) { + p.onBatterySaverChanged(this); + } + if (sendBroadcast) { if (enabled) { // STOPSHIP Remove the toast. diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java new file mode 100644 index 000000000000..0af19b6f2c5d --- /dev/null +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java @@ -0,0 +1,65 @@ +/* + * 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.power.batterysaver; + +import android.content.Context; +import android.provider.Settings; +import android.provider.Settings.Global; +import android.util.Slog; + +import com.android.server.power.BatterySaverPolicy; +import com.android.server.power.batterysaver.BatterySaverController.Plugin; + +public class BatterySaverLocationPlugin implements Plugin { + private static final String TAG = "BatterySaverLocationPlugin"; + + private static final boolean DEBUG = BatterySaverController.DEBUG; + + private final Context mContext; + + public BatterySaverLocationPlugin(Context context) { + mContext = context; + } + + @Override + public void onBatterySaverChanged(BatterySaverController caller) { + if (DEBUG) { + Slog.d(TAG, "onBatterySaverChanged"); + } + updateLocationState(caller); + } + + @Override + public void onSystemReady(BatterySaverController caller) { + if (DEBUG) { + Slog.d(TAG, "onSystemReady"); + } + updateLocationState(caller); + } + + private void updateLocationState(BatterySaverController caller) { + final boolean kill = + (caller.getBatterySaverPolicy().getGpsMode() + == BatterySaverPolicy.GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) && + caller.isEnabled() && !caller.isInteractive(); + + if (DEBUG) { + Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location."); + } + Settings.Global.putInt(mContext.getContentResolver(), + Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0); + } +} diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java index ab9ab6713eea..a8c68c07231d 100644 --- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java +++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java @@ -63,7 +63,7 @@ public class KeyAttestationApplicationIdProviderService PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i], PackageManager.GET_SIGNATURES, userId); keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i], - packageInfo.versionCode, packageInfo.signatures); + packageInfo.getLongVersionCode(), packageInfo.signatures); } } catch (NameNotFoundException nnfe) { throw new RemoteException(nnfe.getMessage()); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 00c208d08883..1ce140004bcd 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -155,6 +155,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return ret; } + private final static long[] toLongArray(List<Long> list) { + long[] ret = new long[list.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = list.get(i); + } + return ret; + } + // Assumes that sStatsdLock is held. private final void informAllUidsLocked(Context context) throws RemoteException { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); @@ -165,7 +173,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } List<Integer> uids = new ArrayList(); - List<Integer> versions = new ArrayList(); + List<Long> versions = new ArrayList(); List<String> apps = new ArrayList(); // Add in all the apps for every user/profile. @@ -174,12 +182,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { for (int j = 0; j < pi.size(); j++) { if (pi.get(j).applicationInfo != null) { uids.add(pi.get(j).applicationInfo.uid); - versions.add(pi.get(j).versionCode); + versions.add(pi.get(j).getLongVersionCode()); apps.add(pi.get(j).packageName); } } } - sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new + sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new String[apps.size()])); if (DEBUG) { Slog.w(TAG, "Sent data for " + uids.size() + " apps"); @@ -222,7 +230,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { int uid = b.getInt(Intent.EXTRA_UID); String app = intent.getData().getSchemeSpecificPart(); PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER); - sStatsd.informOnePackage(app, uid, pi.versionCode); + sStatsd.informOnePackage(app, uid, pi.getLongVersionCode()); } } catch (Exception e) { Slog.w(TAG, "Failed to inform statsd of an app update", e); diff --git a/services/core/java/com/android/server/timezone/CheckToken.java b/services/core/java/com/android/server/timezone/CheckToken.java index 51283608c66e..4c4a8d72f51d 100644 --- a/services/core/java/com/android/server/timezone/CheckToken.java +++ b/services/core/java/com/android/server/timezone/CheckToken.java @@ -46,8 +46,8 @@ final class CheckToken { ByteArrayOutputStream baos = new ByteArrayOutputStream(12 /* (3 * sizeof(int)) */); try (DataOutputStream dos = new DataOutputStream(baos)) { dos.writeInt(mOptimisticLockId); - dos.writeInt(mPackageVersions.mUpdateAppVersion); - dos.writeInt(mPackageVersions.mDataAppVersion); + dos.writeLong(mPackageVersions.mUpdateAppVersion); + dos.writeLong(mPackageVersions.mDataAppVersion); } catch (IOException e) { throw new RuntimeException("Unable to write into a ByteArrayOutputStream", e); } @@ -58,8 +58,8 @@ final class CheckToken { ByteArrayInputStream bais = new ByteArrayInputStream(tokenBytes); try (DataInputStream dis = new DataInputStream(bais)) { int versionId = dis.readInt(); - int updateAppVersion = dis.readInt(); - int dataAppVersion = dis.readInt(); + long updateAppVersion = dis.readLong(); + long dataAppVersion = dis.readLong(); return new CheckToken(versionId, new PackageVersions(updateAppVersion, dataAppVersion)); } } diff --git a/services/core/java/com/android/server/timezone/PackageManagerHelper.java b/services/core/java/com/android/server/timezone/PackageManagerHelper.java index 804941add891..f6e35e8fdd56 100644 --- a/services/core/java/com/android/server/timezone/PackageManagerHelper.java +++ b/services/core/java/com/android/server/timezone/PackageManagerHelper.java @@ -26,7 +26,7 @@ import android.content.pm.PackageManager; */ interface PackageManagerHelper { - int getInstalledPackageVersion(String packageName) + long getInstalledPackageVersion(String packageName) throws PackageManager.NameNotFoundException; boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException; diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java index cac7f7b811bf..5601c91a78e7 100644 --- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java +++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java @@ -76,7 +76,7 @@ final class PackageStatusStorage { */ private static final String ATTRIBUTE_DATA_APP_VERSION = "dataAppPackageVersion"; - private static final int UNKNOWN_PACKAGE_VERSION = -1; + private static final long UNKNOWN_PACKAGE_VERSION = -1; private final AtomicFile mPackageStatusFile; @@ -320,14 +320,14 @@ final class PackageStatusStorage { serializer.attribute(namespace, ATTRIBUTE_CHECK_STATUS, statusAttributeValue); serializer.attribute(namespace, ATTRIBUTE_OPTIMISTIC_LOCK_ID, Integer.toString(optimisticLockId)); - int updateAppVersion = status == null + long updateAppVersion = status == null ? UNKNOWN_PACKAGE_VERSION : packageVersions.mUpdateAppVersion; serializer.attribute(namespace, ATTRIBUTE_UPDATE_APP_VERSION, - Integer.toString(updateAppVersion)); - int dataAppVersion = status == null + Long.toString(updateAppVersion)); + long dataAppVersion = status == null ? UNKNOWN_PACKAGE_VERSION : packageVersions.mDataAppVersion; serializer.attribute(namespace, ATTRIBUTE_DATA_APP_VERSION, - Integer.toString(dataAppVersion)); + Long.toString(dataAppVersion)); serializer.endTag(namespace, TAG_PACKAGE_STATUS); serializer.endDocument(); serializer.flush(); diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index f0306b9b0c38..1c54320f25b8 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -445,8 +445,8 @@ public class PackageTracker { } private PackageVersions lookupInstalledPackageVersions() { - int updatePackageVersion; - int dataPackageVersion; + long updatePackageVersion; + long dataPackageVersion; try { updatePackageVersion = mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName); diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java index b89dd3838ae9..6a330e6acf08 100644 --- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java @@ -81,11 +81,11 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa } @Override - public int getInstalledPackageVersion(String packageName) + public long getInstalledPackageVersion(String packageName) throws PackageManager.NameNotFoundException { int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); - return packageInfo.versionCode; + return packageInfo.getLongVersionCode(); } @Override diff --git a/services/core/java/com/android/server/timezone/PackageVersions.java b/services/core/java/com/android/server/timezone/PackageVersions.java index fc0d6e1ad51e..0084c1a2e5e5 100644 --- a/services/core/java/com/android/server/timezone/PackageVersions.java +++ b/services/core/java/com/android/server/timezone/PackageVersions.java @@ -21,10 +21,10 @@ package com.android.server.timezone; */ final class PackageVersions { - final int mUpdateAppVersion; - final int mDataAppVersion; + final long mUpdateAppVersion; + final long mDataAppVersion; - PackageVersions(int updateAppVersion, int dataAppVersion) { + PackageVersions(long updateAppVersion, long dataAppVersion) { this.mUpdateAppVersion = updateAppVersion; this.mDataAppVersion = dataAppVersion; } @@ -48,8 +48,8 @@ final class PackageVersions { @Override public int hashCode() { - int result = mUpdateAppVersion; - result = 31 * result + mDataAppVersion; + int result = Long.hashCode(mUpdateAppVersion); + result = 31 * result + Long.hashCode(mDataAppVersion); return result; } diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java index 1e334b83d8b0..4aa2b3722749 100644 --- a/services/core/java/com/android/server/webkit/SystemImpl.java +++ b/services/core/java/com/android/server/webkit/SystemImpl.java @@ -156,9 +156,10 @@ public class SystemImpl implements SystemInterface { return mWebViewProviderPackages; } - public int getFactoryPackageVersion(String packageName) throws NameNotFoundException { + public long getFactoryPackageVersion(String packageName) throws NameNotFoundException { PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); - return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode; + return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY) + .getLongVersionCode(); } /** diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java index d57edcd4c44c..405fe7d41e61 100644 --- a/services/core/java/com/android/server/webkit/SystemInterface.java +++ b/services/core/java/com/android/server/webkit/SystemInterface.java @@ -36,7 +36,7 @@ import java.util.List; public interface SystemInterface { public WebViewProviderInfo[] getWebViewPackages(); public int onWebViewProviderChanged(PackageInfo packageInfo); - public int getFactoryPackageVersion(String packageName) throws NameNotFoundException; + public long getFactoryPackageVersion(String packageName) throws NameNotFoundException; public String getUserChosenWebViewProvider(Context context); public void updateUserSetting(Context context, String newProviderName); diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java index 7fc907f95c93..7e05e46a2637 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdater.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java @@ -54,7 +54,7 @@ class WebViewUpdater { private Context mContext; private SystemInterface mSystemInterface; - private int mMinimumVersionCode = -1; + private long mMinimumVersionCode = -1; // Keeps track of the number of running relro creations private int mNumRelroCreationsStarted = 0; @@ -430,7 +430,7 @@ class WebViewUpdater { if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) { return VALIDITY_INCORRECT_SDK_VERSION; } - if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode()) + if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode()) && !mSystemInterface.systemIsDebuggable()) { // Webview providers may be downgraded arbitrarily low, prevent that by enforcing // minimum version code. This check is only enforced for user builds. @@ -461,9 +461,9 @@ class WebViewUpdater { * * @return true if versionCode1 is higher than or equal to versionCode2. */ - private static boolean versionCodeGE(int versionCode1, int versionCode2) { - int v1 = versionCode1 / 100000; - int v2 = versionCode2 / 100000; + private static boolean versionCodeGE(long versionCode1, long versionCode2) { + long v1 = versionCode1 / 100000; + long v2 = versionCode2 / 100000; return v1 >= v2; } @@ -478,16 +478,16 @@ class WebViewUpdater { * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not * hold mLock meaning that we must take extra care to ensure this method is thread-safe. */ - private int getMinimumVersionCode() { + private long getMinimumVersionCode() { if (mMinimumVersionCode > 0) { return mMinimumVersionCode; } - int minimumVersionCode = -1; + long minimumVersionCode = -1; for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { if (provider.availableByDefault && !provider.isFallback) { try { - int versionCode = + long versionCode = mSystemInterface.getFactoryPackageVersion(provider.packageName); if (minimumVersionCode < 0 || versionCode < minimumVersionCode) { minimumVersionCode = versionCode; @@ -577,7 +577,7 @@ class WebViewUpdater { String packageDetails = String.format( "versionName: %s, versionCode: %d, targetSdkVersion: %d", systemUserPackageInfo.versionName, - systemUserPackageInfo.versionCode, + systemUserPackageInfo.getLongVersionCode(), systemUserPackageInfo.applicationInfo.targetSdkVersion); if (validity == VALIDITY_OK) { boolean installedForAllUsers = isInstalledAndEnabledForAllUsers( diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 43a089373a35..c09115794713 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -236,7 +236,7 @@ class TaskSnapshotController { return null; } return new TaskSnapshot(buffer, top.getConfiguration().orientation, - minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), + getInsetsFromTaskBounds(mainWindow, task), isLowRamDevice /* reduced */, scaleFraction /* scale */); } @@ -244,11 +244,18 @@ class TaskSnapshotController { return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT; } - private Rect minRect(Rect rect1, Rect rect2) { - return new Rect(Math.min(rect1.left, rect2.left), - Math.min(rect1.top, rect2.top), - Math.min(rect1.right, rect2.right), - Math.min(rect1.bottom, rect2.bottom)); + private Rect getInsetsFromTaskBounds(WindowState state, Task task) { + final Rect r = new Rect(); + r.set(state.getContentFrameLw()); + r.intersectUnchecked(state.getStableFrameLw()); + + final Rect taskBounds = task.getBounds(); + + r.set(Math.max(0, r.left - taskBounds.left), + Math.max(0, r.top - taskBounds.top), + Math.max(0, taskBounds.right - r.right), + Math.max(0, taskBounds.bottom - r.bottom)); + return r; } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 41915a32704d..57f754f8234d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -349,8 +349,9 @@ class TaskSnapshotSurface implements StartingSurface { // Let's remove all system decorations except the status bar, but only if the task is at the // very top of the screen. + final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; rect.inset((int) (insets.left * mSnapshot.getScale()), - mTaskBounds.top != 0 ? (int) (insets.top * mSnapshot.getScale()) : 0, + isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), (int) (insets.right * mSnapshot.getScale()), (int) (insets.bottom * mSnapshot.getScale())); return rect; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 663083c41790..ddc0c2339050 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -736,6 +736,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_ORGANIZATION_NAME = "organization-name"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; + private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled"; final DeviceAdminInfo info; @@ -785,6 +786,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean requireAutoTime = false; // Can only be set by a device owner. boolean forceEphemeralUsers = false; // Can only be set by a device owner. boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner. + boolean isLogoutButtonEnabled = false; // Can only be set by a device owner. // one notification after enabling + one more after reboots static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2; @@ -1102,6 +1104,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.text(organizationName); out.endTag(null, TAG_ORGANIZATION_NAME); } + if (isLogoutButtonEnabled) { + out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED); + out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled)); + out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED); + } } void writePackageListToXml(XmlSerializer out, String outerTag, @@ -1275,6 +1282,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else { Log.w(LOG_TAG, "Missing text when loading organization name"); } + } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) { + isLogoutButtonEnabled = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -11433,4 +11443,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return false; } + + @Override + public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(admin); + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + + if (enabled == isLogoutButtonEnabledInternalLocked()) { + // already in the requested state + return; + } + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + deviceOwner.isLogoutButtonEnabled = enabled; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + + @Override + public boolean isLogoutButtonEnabled() { + if (!mHasFeature) { + return false; + } + synchronized (this) { + return isLogoutButtonEnabledInternalLocked(); + } + } + + private boolean isLogoutButtonEnabledInternalLocked() { + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled; + } + } diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index e8ae020c2728..364bbc035a29 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -25,6 +25,7 @@ import static com.android.internal.print.DumpUtils.writePrintJobInfo; import static com.android.internal.print.DumpUtils.writePrinterId; import static com.android.internal.print.DumpUtils.writePrinterInfo; import static com.android.internal.print.DumpUtils.writeStringIfNotNull; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.NonNull; import android.annotation.Nullable; @@ -81,7 +82,6 @@ import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.BackgroundThread; -import com.android.internal.os.SomeArgs; import com.android.server.print.RemotePrintService.PrintServiceCallbacks; import com.android.server.print.RemotePrintServiceRecommendationService .RemotePrintServiceRecommendationServiceCallbacks; @@ -462,7 +462,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, if (mPrinterDiscoverySession == null) { // If we do not have a session, tell all service to create one. - mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) { + mPrinterDiscoverySession = new PrinterDiscoverySessionMediator() { @Override public void onDestroyed() { mPrinterDiscoverySession = null; @@ -1141,12 +1141,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, // just died. Do this off the main thread since we do to allow // calls into the spooler on the main thread. if (Looper.getMainLooper().isCurrentThread()) { - BackgroundThread.getHandler().post(new Runnable() { - @Override - public void run() { - failScheduledPrintJobsForServiceInternal(serviceName); - } - }); + BackgroundThread.getHandler().sendMessage(obtainMessage( + UserState::failScheduledPrintJobsForServiceInternal, this, serviceName)); } else { failScheduledPrintJobsForServiceInternal(serviceName); } @@ -1341,18 +1337,13 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>(); - private final Handler mSessionHandler; - private boolean mIsDestroyed; - public PrinterDiscoverySessionMediator(Context context) { - mSessionHandler = new SessionHandler(context.getMainLooper()); + PrinterDiscoverySessionMediator() { // Kick off the session creation. - List<RemotePrintService> services = new ArrayList<RemotePrintService>( - mActiveServices.values()); - mSessionHandler.obtainMessage(SessionHandler - .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleDispatchCreatePrinterDiscoverySession, + this, new ArrayList<>(mActiveServices.values()))); } public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { @@ -1361,12 +1352,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, // Bring the added observer up to speed with the printers. if (!mPrinters.isEmpty()) { - List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values()); - SomeArgs args = SomeArgs.obtain(); - args.arg1 = observer; - args.arg2 = printers; - mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED, - args).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handlePrintersAdded, + this, observer, new ArrayList<>(mPrinters.values()))); } } @@ -1403,14 +1391,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, return; } - List<RemotePrintService> services = new ArrayList<RemotePrintService>( - mActiveServices.values()); - SomeArgs args = SomeArgs.obtain(); - args.arg1 = services; - args.arg2 = priorityList; - mSessionHandler.obtainMessage(SessionHandler - .MSG_DISPATCH_START_PRINTER_DISCOVERY, args) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleDispatchStartPrinterDiscovery, this, + new ArrayList<>(mActiveServices.values()), priorityList)); } public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { @@ -1426,11 +1409,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, if (!mStartedPrinterDiscoveryTokens.isEmpty()) { return; } - List<RemotePrintService> services = new ArrayList<RemotePrintService>( - mActiveServices.values()); - mSessionHandler.obtainMessage(SessionHandler - .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleDispatchStopPrinterDiscovery, + this, new ArrayList<>(mActiveServices.values()))); } public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { @@ -1461,12 +1442,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, // Schedule a notification of the service. RemotePrintService service = mActiveServices.get(serviceName); if (service != null) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = service; - args.arg2 = updateList; - mSessionHandler.obtainMessage(SessionHandler - .MSG_VALIDATE_PRINTERS, args) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handleValidatePrinters, + this, service, updateList)); } } } @@ -1493,12 +1471,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, return; } // Ask the service to start tracking. - SomeArgs args = SomeArgs.obtain(); - args.arg1 = service; - args.arg2 = printerId; - mSessionHandler.obtainMessage(SessionHandler - .MSG_START_PRINTER_STATE_TRACKING, args) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleStartPrinterStateTracking, this, service, printerId)); } public final void stopPrinterStateTrackingLocked(PrinterId printerId) { @@ -1520,12 +1494,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, return; } // Ask the service to start tracking. - SomeArgs args = SomeArgs.obtain(); - args.arg1 = service; - args.arg2 = printerId; - mSessionHandler.obtainMessage(SessionHandler - .MSG_STOP_PRINTER_STATE_TRACKING, args) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleStopPrinterStateTracking, this, service, printerId)); } public void onDestroyed() { @@ -1551,11 +1521,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token)); } // Tell the services we are done. - List<RemotePrintService> services = new ArrayList<RemotePrintService>( - mActiveServices.values()); - mSessionHandler.obtainMessage(SessionHandler - .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator:: + handleDispatchDestroyPrinterDiscoverySession, + this, new ArrayList<>(mActiveServices.values()))); } public void onPrintersAddedLocked(List<PrinterInfo> printers) { @@ -1579,8 +1547,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, } } if (addedPrinters != null) { - mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, - addedPrinters).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded, + this, addedPrinters)); } } @@ -1604,8 +1573,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, } } if (removedPrinterIds != null) { - mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, - removedPrinterIds).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved, + this, removedPrinterIds)); } } @@ -1646,8 +1616,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1); addedPrinters.add(newPrinter); - mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, - addedPrinters).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded, + this, addedPrinters)); } } @@ -1661,26 +1632,20 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, return; } // Tell the service to create a session. - mSessionHandler.obtainMessage( - SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION, - service).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + RemotePrintService::createPrinterDiscoverySession, service)); // Start printer discovery if necessary. if (!mStartedPrinterDiscoveryTokens.isEmpty()) { - mSessionHandler.obtainMessage( - SessionHandler.MSG_START_PRINTER_DISCOVERY, - service).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + RemotePrintService::startPrinterDiscovery, service, null)); } // Start tracking printers if necessary final int trackedPrinterCount = mStateTrackedPrinters.size(); for (int i = 0; i < trackedPrinterCount; i++) { PrinterId printerId = mStateTrackedPrinters.get(i); if (printerId.getServiceName().equals(service.getComponentName())) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = service; - args.arg2 = printerId; - mSessionHandler.obtainMessage(SessionHandler - .MSG_START_PRINTER_STATE_TRACKING, args) - .sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + RemotePrintService::startPrinterStateTracking, service, printerId)); } } } @@ -1781,9 +1746,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, for (int i = 0; i < removedPrinterCount; i++) { mPrinters.remove(removedPrinterIds.get(i)); } - mSessionHandler.obtainMessage( - SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, - removedPrinterIds).sendToTarget(); + Handler.getMain().sendMessage(obtainMessage( + UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved, + this, removedPrinterIds)); } } @@ -1873,134 +1838,6 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, Log.e(LOG_TAG, "Error sending removed printers", re); } } - - private final class SessionHandler extends Handler { - public static final int MSG_PRINTERS_ADDED = 1; - public static final int MSG_PRINTERS_REMOVED = 2; - public static final int MSG_DISPATCH_PRINTERS_ADDED = 3; - public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; - - public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5; - public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6; - public static final int MSG_START_PRINTER_DISCOVERY = 7; - public static final int MSG_STOP_PRINTER_DISCOVERY = 8; - public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9; - public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10; - public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11; - public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12; - public static final int MSG_VALIDATE_PRINTERS = 13; - public static final int MSG_START_PRINTER_STATE_TRACKING = 14; - public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15; - public static final int MSG_DESTROY_SERVICE = 16; - - SessionHandler(Looper looper) { - super(looper, null, false); - } - - @Override - @SuppressWarnings("unchecked") - public void handleMessage(Message message) { - switch (message.what) { - case MSG_PRINTERS_ADDED: { - SomeArgs args = (SomeArgs) message.obj; - IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; - List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2; - args.recycle(); - handlePrintersAdded(observer, addedPrinters); - } break; - - case MSG_PRINTERS_REMOVED: { - SomeArgs args = (SomeArgs) message.obj; - IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; - List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2; - args.recycle(); - handlePrintersRemoved(observer, removedPrinterIds); - } - - case MSG_DISPATCH_PRINTERS_ADDED: { - List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj; - handleDispatchPrintersAdded(addedPrinters); - } break; - - case MSG_DISPATCH_PRINTERS_REMOVED: { - List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj; - handleDispatchPrintersRemoved(removedPrinterIds); - } break; - - case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { - RemotePrintService service = (RemotePrintService) message.obj; - service.createPrinterDiscoverySession(); - } break; - - case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { - RemotePrintService service = (RemotePrintService) message.obj; - service.destroyPrinterDiscoverySession(); - } break; - - case MSG_START_PRINTER_DISCOVERY: { - RemotePrintService service = (RemotePrintService) message.obj; - service.startPrinterDiscovery(null); - } break; - - case MSG_STOP_PRINTER_DISCOVERY: { - RemotePrintService service = (RemotePrintService) message.obj; - service.stopPrinterDiscovery(); - } break; - - case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { - List<RemotePrintService> services = (List<RemotePrintService>) message.obj; - handleDispatchCreatePrinterDiscoverySession(services); - } break; - - case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { - List<RemotePrintService> services = (List<RemotePrintService>) message.obj; - handleDispatchDestroyPrinterDiscoverySession(services); - } break; - - case MSG_DISPATCH_START_PRINTER_DISCOVERY: { - SomeArgs args = (SomeArgs) message.obj; - List<RemotePrintService> services = (List<RemotePrintService>) args.arg1; - List<PrinterId> printerIds = (List<PrinterId>) args.arg2; - args.recycle(); - handleDispatchStartPrinterDiscovery(services, printerIds); - } break; - - case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { - List<RemotePrintService> services = (List<RemotePrintService>) message.obj; - handleDispatchStopPrinterDiscovery(services); - } break; - - case MSG_VALIDATE_PRINTERS: { - SomeArgs args = (SomeArgs) message.obj; - RemotePrintService service = (RemotePrintService) args.arg1; - List<PrinterId> printerIds = (List<PrinterId>) args.arg2; - args.recycle(); - handleValidatePrinters(service, printerIds); - } break; - - case MSG_START_PRINTER_STATE_TRACKING: { - SomeArgs args = (SomeArgs) message.obj; - RemotePrintService service = (RemotePrintService) args.arg1; - PrinterId printerId = (PrinterId) args.arg2; - args.recycle(); - handleStartPrinterStateTracking(service, printerId); - } break; - - case MSG_STOP_PRINTER_STATE_TRACKING: { - SomeArgs args = (SomeArgs) message.obj; - RemotePrintService service = (RemotePrintService) args.arg1; - PrinterId printerId = (PrinterId) args.arg2; - args.recycle(); - handleStopPrinterStateTracking(service, printerId); - } break; - - case MSG_DESTROY_SERVICE: { - RemotePrintService service = (RemotePrintService) message.obj; - service.destroy(); - } break; - } - } - } } private final class PrintJobForAppCache { diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java index cbda12da1a6f..4eb42201f072 100644 --- a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java +++ b/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java @@ -21,10 +21,10 @@ import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Slog; import org.junit.Before; import org.junit.Test; diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java index ddf46a052e40..ba5ad8159029 100644 --- a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java +++ b/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Looper; import android.service.notification.Condition; +import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; import android.support.test.InstrumentationRegistry; import android.test.ServiceTestCase; @@ -34,7 +35,9 @@ public class ScheduleConditionProviderTest extends ServiceTestCase<ScheduleCondi @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - Looper.prepare(); + if (Looper.myLooper() == null) { + Looper.prepare(); + } Intent startIntent = new Intent("com.android.server.notification.ScheduleConditionProvider"); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java index 56765101baa9..7160d7fc8c2f 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java @@ -19,31 +19,16 @@ package com.android.server.am; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import android.app.ActivityOptions; -import android.app.IApplicationThread; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ResolveInfo; -import android.os.IBinder; import android.platform.test.annotations.Presubmit; -import android.service.voice.IVoiceInteractionSession; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import com.android.internal.app.IVoiceInteractor; import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch; import com.android.server.am.ActivityStarter.Factory; import org.junit.runner.RunWith; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyObject; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -73,59 +58,10 @@ public class ActivityStartControllerTests extends ActivityTestsBase { super.setUp(); mService = createActivityManagerService(); mFactory = mock(Factory.class); - mStarter = mock(ActivityStarter.class); - doReturn(mStarter).when(mFactory).getStarter(any(), any(), any(), any()); mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory); - } - - /** - * Ensures that the starter is correctly invoked on - * {@link ActivityStartController#startActivity} - */ - @Test - @Presubmit - public void testStartActivity() { - final Random random = new Random(); - - final IApplicationThread applicationThread = mock(IApplicationThread.class); - final Intent intent = mock(Intent.class); - final Intent ephemeralIntent = mock(Intent.class); - final String resolvedType = "TestType"; - final ActivityInfo aInfo = mock(ActivityInfo.class); - final ResolveInfo rInfo = mock(ResolveInfo.class); - final IVoiceInteractionSession voiceInteractionSession = - mock(IVoiceInteractionSession.class); - final IVoiceInteractor voiceInteractor = mock(IVoiceInteractor.class); - final IBinder resultTo = mock(IBinder.class); - final String resultWho = "resultWho"; - final int requestCode = random.nextInt(); - final int callingPid = random.nextInt(); - final int callingUid = random.nextInt(); - final String callingPackage = "callingPackage"; - final int realCallingPid = random.nextInt(); - final int realCallingUid = random.nextInt(); - final int startFlags = random.nextInt(); - final ActivityOptions options = mock(ActivityOptions.class); - final boolean ignoreTargetSecurity = random.nextBoolean(); - final boolean componentSpecified = random.nextBoolean(); - final ActivityRecord[] outActivity = new ActivityRecord[1]; - final TaskRecord inTask = mock(TaskRecord.class); - final String reason ="reason"; - - mController.startActivity(applicationThread, intent, ephemeralIntent, resolvedType, - aInfo, rInfo, voiceInteractionSession, voiceInteractor, resultTo, resultWho, - requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, - startFlags, options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, - reason); - - // The starter should receive a start command with the originally provided parameters - verify(mStarter, times(1)).startActivityLocked(eq(applicationThread), eq(intent), - eq(ephemeralIntent), eq(resolvedType), eq(aInfo), eq(rInfo), - eq(voiceInteractionSession), eq(voiceInteractor), eq(resultTo), eq(resultWho), - eq(requestCode), eq(callingPid), eq(callingUid), eq(callingPackage), - eq(realCallingPid), eq(realCallingUid), eq(startFlags), eq(options), - eq(ignoreTargetSecurity), eq(componentSpecified), eq(outActivity), eq(inTask), - eq(reason)); + mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor, + mock(ActivityStartInterceptor.class))); + doReturn(mStarter).when(mFactory).obtainStarter(); } /** @@ -149,7 +85,7 @@ public class ActivityStartControllerTests extends ActivityTestsBase { final boolean resume = random.nextBoolean(); mController.doPendingActivityLaunches(resume); - verify(mStarter, times(1)).startActivity(eq(activity), eq(source), eq(null), + verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null), eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null)); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index 471726b2979a..e8194dd8e777 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -166,10 +166,9 @@ public class ActivityStarterTests extends ActivityTestsBase { * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP}) * and the launch flags specified in the intent. The method constructs a call to - * {@link ActivityStarter#startActivityLocked} based on these preconditions and ensures the - * result matches the expected. It is important to note that the method also checks side effects - * of the start, such as ensuring {@link ActivityOptions#abort()} is called in the relevant - * scenarios. + * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches + * the expected. It is important to note that the method also checks side effects of the start, + * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios. * @param preconditions A bitmask representing the preconditions for the launch * @param launchFlags The launch flags to be provided by the launch {@link Intent}. * @param expectedResult The expected result from the launch. @@ -254,14 +253,13 @@ public class ActivityStarterTests extends ActivityTestsBase { final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE) ? 1 : 0; - final int result = starter.startActivityLocked(caller, intent, - null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/, - null /*voiceSession*/, null /*voiceInteractor*/, resultTo, - null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/, - null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/, - 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/, - false /*componentSpecified*/, null /*outActivity*/, - null /*inTask*/, "testLaunchActivityPermissionDenied"); + final int result = starter.setCaller(caller) + .setIntent(intent) + .setActivityInfo(aInfo) + .setResultTo(resultTo) + .setRequestCode(requestCode) + .setReason("testLaunchActivityPermissionDenied") + .execute(); // In some cases the expected result internally is different than the published result. We // must use ActivityStarter#getExternalResult to translate. @@ -269,15 +267,18 @@ public class ActivityStarterTests extends ActivityTestsBase { // Ensure that {@link ActivityOptions} are aborted with unsuccessful result. if (expectedResult != START_SUCCESS) { + final ActivityStarter optionStarter = new ActivityStarter(mController, mService, + mService.mStackSupervisor, mock(ActivityStartInterceptor.class)); final ActivityOptions options = spy(ActivityOptions.makeBasic()); - final int optionResult = starter.startActivityLocked(caller, intent, - null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/, - null /*voiceSession*/, null /*voiceInteractor*/, resultTo, - null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/, - null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/, - 0 /*startFlags*/, options /*options*/, false /*ignoreTargetSecurity*/, - false /*componentSpecified*/, null /*outActivity*/, - null /*inTask*/, "testLaunchActivityPermissionDenied"); + + final int optionResult = optionStarter.setCaller(caller) + .setIntent(intent) + .setActivityInfo(aInfo) + .setResultTo(resultTo) + .setRequestCode(requestCode) + .setReason("testLaunchActivityPermissionDenied") + .setActivityOptions(options) + .execute(); verify(options, times(1)).abort(); } } diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java index 87c587a241af..c40b411a21d7 100644 --- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java @@ -20,6 +20,7 @@ import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY; import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_ID; import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION; import static com.google.common.truth.Truth.assertThat; @@ -102,11 +103,40 @@ public class BackupManagerMonitorUtilsTest { ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mMonitorMock).onEvent(bundleCaptor.capture()); Bundle eventBundle = bundleCaptor.getValue(); - assertThat(eventBundle.size()).isEqualTo(6); + assertThat(eventBundle.size()).isEqualTo(7); assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1); assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2); assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package"); assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3); + assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo(3); + assertThat(eventBundle.getInt("key1")).isEqualTo(4); + assertThat(eventBundle.getString("key2")).isEqualTo("value2"); + } + + @Test + public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectlyLong() throws Exception { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = "test.package"; + packageInfo.versionCode = 3; + packageInfo.versionCodeMajor = 10; + Bundle extras = new Bundle(); + extras.putInt("key1", 4); + extras.putString("key2", "value2"); + + IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1, + packageInfo, 2, extras); + + assertThat(result).isEqualTo(mMonitorMock); + ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mMonitorMock).onEvent(bundleCaptor.capture()); + Bundle eventBundle = bundleCaptor.getValue(); + assertThat(eventBundle.size()).isEqualTo(7); + assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1); + assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2); + assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package"); + assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3); + assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo( + (10L << 32) | 3); assertThat(eventBundle.getInt("key1")).isEqualTo(4); assertThat(eventBundle.getString("key2")).isEqualTo("value2"); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 7e11e87b9f22..e2ba4d5f4aa8 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -103,7 +103,7 @@ public class DpmMockContext extends MockContext { long callingIdentity = clearCallingIdentity(); Throwable throwableToPropagate = null; try { - action.run(); + action.runOrThrow(); } catch (Throwable throwable) { throwableToPropagate = throwable; } finally { diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 025ebc36f99b..926a9112ad29 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -1075,6 +1075,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { final PackageInfo ret = new PackageInfo(); ret.packageName = pi.packageName; ret.versionCode = pi.versionCode; + ret.versionCodeMajor = pi.versionCodeMajor; ret.lastUpdateTime = pi.lastUpdateTime; ret.applicationInfo = new ApplicationInfo(pi.applicationInfo); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index fd105bc8ef1c..0995f2e35d50 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -577,7 +577,7 @@ public class PackageManagerSettingsTests { new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); pkg.usesStaticLibraries = new ArrayList<>( Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3")); - pkg.usesStaticLibrariesVersions = new int[] {2, 4, 6}; + pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6}; settings.insertPackageSettingLPw(ps, pkg); assertEquals(pkg, ps.pkg); assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries); @@ -602,6 +602,11 @@ public class PackageManagerSettingsTests { Arrays.equals(a, b)); } + private void assertArrayEquals(long[] a, long[] b) { + assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b), + Arrays.equals(a, b)); + } + private void verifyUserState(PackageUserState userState, PackageUserState oldUserState, boolean userStateChanged) { verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 1f9a243d0f22..36cc3a059eac 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -471,7 +471,7 @@ public class PackageParserTest { pkg.usesStaticLibraries.add("foo23"); pkg.usesStaticLibrariesCertDigests = new String[1][]; pkg.usesStaticLibrariesCertDigests[0] = new String[] { "digest" }; - pkg.usesStaticLibrariesVersions = new int[] { 100 }; + pkg.usesStaticLibrariesVersions = new long[] { 100 }; pkg.libraryNames = new ArrayList<>(); pkg.libraryNames.add("foo10"); 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 7d73e82eaf76..0ea8d4f5e353 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java @@ -1246,13 +1246,13 @@ public class PackageTrackerTest { } private void configureUpdateAppPackageVersion(String updateAppPackageName, - int updataAppPackageVersion) throws Exception { + long updataAppPackageVersion) throws Exception { when(mMockPackageManagerHelper.getInstalledPackageVersion(updateAppPackageName)) .thenReturn(updataAppPackageVersion); } private void configureDataAppPackageVersion(String dataAppPackageName, - int dataAppPackageVersion) throws Exception { + long dataAppPackageVersion) throws Exception { when(mMockPackageManagerHelper.getInstalledPackageVersion(dataAppPackageName)) .thenReturn(dataAppPackageVersion); } diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java index cd3ae1ae8b03..26853a9a7fa5 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java +++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java @@ -165,7 +165,7 @@ public class TestSystemImpl implements SystemInterface { } @Override - public int getFactoryPackageVersion(String packageName) throws NameNotFoundException { + public long getFactoryPackageVersion(String packageName) throws NameNotFoundException { PackageInfo pi = null; Map<Integer, PackageInfo> userPackages = mPackages.get(packageName); if (userPackages == null) throw new NameNotFoundException(); diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java index 2224de56f21c..401f585886a2 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java @@ -1512,8 +1512,8 @@ public class WebViewUpdateServiceTest { // Ensure the API is correct before running waitForAndGetProvider assertEquals(firstPackage.packageName, mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); - assertEquals(firstPackage.versionCode, - mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode); + assertEquals(firstPackage.getLongVersionCode(), + mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode()); assertEquals(firstPackage.versionName, mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName); @@ -1524,8 +1524,8 @@ public class WebViewUpdateServiceTest { // Ensure the API is still correct after running waitForAndGetProvider assertEquals(firstPackage.packageName, mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); - assertEquals(firstPackage.versionCode, - mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode); + assertEquals(firstPackage.getLongVersionCode(), + mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode()); assertEquals(firstPackage.versionName, mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 2bb1c4ed648c..3c32614815cb 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -23,7 +23,6 @@ import com.android.internal.telecom.IVideoProvider; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Notification; import android.bluetooth.BluetoothDevice; import android.content.Intent; @@ -397,9 +396,7 @@ public abstract class Connection extends Conferenceable { /** * Set by the framework to indicate that a connection has an active RTT session associated with * it. - * @hide */ - @TestApi public static final int PROPERTY_IS_RTT = 1 << 8; //********************************************************************************************** @@ -831,9 +828,7 @@ public abstract class Connection extends Conferenceable { /** * Provides methods to read and write RTT data to/from the in-call app. - * @hide */ - @TestApi public static final class RttTextStream { private static final int READ_BUFFER_SIZE = 1000; private final InputStreamReader mPipeFromInCall; @@ -2608,10 +2603,8 @@ public abstract class Connection extends Conferenceable { /** * Informs listeners that a previously requested RTT session via * {@link ConnectionRequest#isRequestingRtt()} or - * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded. - * @hide + * {@link #onStartRtt(RttTextStream)} has succeeded. */ - @TestApi public final void sendRttInitiationSuccess() { setRttProperty(); mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this)); @@ -2619,14 +2612,11 @@ public abstract class Connection extends Conferenceable { /** * Informs listeners that a previously requested RTT session via - * {@link ConnectionRequest#isRequestingRtt()} or - * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} + * {@link ConnectionRequest#isRequestingRtt()} or {@link #onStartRtt(RttTextStream)} * has failed. * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the * exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}. - * @hide */ - @TestApi public final void sendRttInitiationFailure(int reason) { unsetRttProperty(); mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason)); @@ -2635,9 +2625,7 @@ public abstract class Connection extends Conferenceable { /** * Informs listeners that a currently active RTT session has been terminated by the remote * side of the coll. - * @hide */ - @TestApi public final void sendRttSessionRemotelyTerminated() { mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this)); } @@ -2645,9 +2633,7 @@ public abstract class Connection extends Conferenceable { /** * Informs listeners that the remote side of the call has requested an upgrade to include an * RTT session in the call. - * @hide */ - @TestApi public final void sendRemoteRttRequest() { mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this)); } @@ -2864,17 +2850,13 @@ public abstract class Connection extends Conferenceable { * request, respectively. * @param rttTextStream The object that should be used to send text to or receive text from * the in-call app. - * @hide */ - @TestApi public void onStartRtt(@NonNull RttTextStream rttTextStream) {} /** * Notifies this {@link Connection} that it should terminate any existing RTT communication * channel. No response to Telecom is needed for this method. - * @hide */ - @TestApi public void onStopRtt() {} /** @@ -2882,11 +2864,9 @@ public abstract class Connection extends Conferenceable { * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is * indicated by the supplied {@link RttTextStream} being non-null, and rejection is * indicated by {@code rttTextStream} being {@code null} - * @hide * @param rttTextStream The object that should be used to send text to or receive text from * the in-call app. */ - @TestApi public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} /** diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index e169e5f80741..658b4734b0b5 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -16,7 +16,6 @@ package android.telecom; -import android.annotation.TestApi; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; @@ -310,9 +309,7 @@ public final class ConnectionRequest implements Parcelable { * send and receive RTT text to/from the in-call app. * @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null} * if this connection request is not requesting an RTT session upon connection establishment. - * @hide */ - @TestApi public Connection.RttTextStream getRttTextStream() { if (isRequestingRtt()) { return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); @@ -324,9 +321,7 @@ public final class ConnectionRequest implements Parcelable { /** * Convenience method for determining whether the ConnectionRequest is requesting an RTT session * @return {@code true} if RTT is requested, {@code false} otherwise. - * @hide */ - @TestApi public boolean isRequestingRtt() { return mRttPipeFromInCall != null && mRttPipeToInCall != null; } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 9ccfa942af5f..c7e51310e106 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -408,7 +408,7 @@ public class PhoneStateListener { /** * Callback invoked when device call state changes. * @param state call state - * @param incomingNumber incoming call phone number. If application does not have + * @param phoneNumber call phone number. If application does not have * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty * string will be passed as an argument. * @@ -416,7 +416,7 @@ public class PhoneStateListener { * @see TelephonyManager#CALL_STATE_RINGING * @see TelephonyManager#CALL_STATE_OFFHOOK */ - public void onCallStateChanged(int state, String incomingNumber) { + public void onCallStateChanged(int state, String phoneNumber) { // default implementation empty } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 81806e52f0d8..99fc9b33eba8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6641,6 +6641,7 @@ public class TelephonyManager { * @return PRLVersion or null if error. * @hide */ + @SystemApi public String getCdmaPrlVersion() { return getCdmaPrlVersion(getSubId()); } diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java index 0c562e65bc85..ce8019f85632 100644 --- a/test-mock/src/android/test/mock/MockPackageManager.java +++ b/test-mock/src/android/test/mock/MockPackageManager.java @@ -46,6 +46,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; +import android.content.pm.dex.ArtManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Rect; @@ -1174,4 +1175,12 @@ public class MockPackageManager extends PackageManager { @Nullable DexModuleRegisterCallback callback) { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + @Override + public ArtManager getArtManager() { + throw new UnsupportedOperationException(); + } } diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk index 88794c26520e..202a699c1706 100644 --- a/tests/backup/Android.mk +++ b/tests/backup/Android.mk @@ -20,7 +20,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ backup_helper_test.cpp - + +LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE_TAGS := optional LOCAL_MODULE := backup_helper_test LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) @@ -43,4 +44,3 @@ LOCAL_PACKAGE_NAME := BackupTest LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) - diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp index b5f6ff57e6d9..457dcc40a607 100644 --- a/tests/backup/backup_helper_test.cpp +++ b/tests/backup/backup_helper_test.cpp @@ -118,7 +118,7 @@ main(int argc, const char** argv) #else int -main(int argc, char** argv) +main(int, char**) { printf ("test_backup_helper built without the tests\n"); return 0; diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 8e579aa68eae..0720886f8816 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -270,8 +270,8 @@ public class IpSecServiceTest { } /** - * This function checks if the number of encap UDP socket that one UID can reserve - * has a reasonable limit. + * This function checks if the number of encap UDP socket that one UID can reserve has a + * reasonable limit. */ @Test public void testSocketResourceTrackerLimitation() throws Exception { @@ -287,9 +287,10 @@ public class IpSecServiceTest { openUdpEncapSockets.add(newUdpEncapSocket); } // Assert that the total sockets quota has a reasonable limit. + assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty()); assertTrue( - openUdpEncapSockets.size() > 0 - && openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS); + "Number of open UDP encap sockets is out of bound", + openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS); // Try to reserve one more UDP encapsulation socket, and should fail. IpSecUdpEncapResponse extraUdpEncapSocket = @@ -297,7 +298,7 @@ public class IpSecServiceTest { assertNotNull(extraUdpEncapSocket); assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status); - // Close one of the open UDP encapsulation scokets. + // Close one of the open UDP encapsulation sockets. mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId); openUdpEncapSockets.get(0).fileDescriptor.close(); openUdpEncapSockets.remove(0); @@ -316,10 +317,9 @@ public class IpSecServiceTest { } /** - * This function checks if the number of SPI that one UID can reserve - * has a reasonable limit. - * This test does not test for both address families or duplicate SPIs because resource - * tracking code does not depend on them. + * This function checks if the number of SPI that one UID can reserve has a reasonable limit. + * This test does not test for both address families or duplicate SPIs because resource tracking + * code does not depend on them. */ @Test public void testSpiResourceTrackerLimitation() throws Exception { diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index a93ee2e2b71d..deb9cc083eb0 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -68,6 +68,7 @@ public: mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL), mBuildSharedLibrary(false), mBuildAppAsSharedLibrary(false), + mCompileSdkVersion(0), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -123,6 +124,10 @@ public: void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; } bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; } void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; } + const android::String8& getCompileSdkVersionCodename() { return mCompileSdkVersionCodename; } + void setCompileSdkVersionCodename(const android::String8& codename) { mCompileSdkVersionCodename = codename; } + int getCompileSdkVersion() { return mCompileSdkVersion; } + void setCompileSdkVersion(int version) { mCompileSdkVersion = version; } const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; } void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; } const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; } @@ -344,6 +349,8 @@ private: const char* mSingleCrunchOutputFile; bool mBuildSharedLibrary; bool mBuildAppAsSharedLibrary; + int mCompileSdkVersion; + android::String8 mCompileSdkVersionCodename; android::String8 mPlatformVersionCode; android::String8 mPlatformVersionName; android::String8 mPrivateSymbolsPackage; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index cb87737c6868..05375b0cb871 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -293,6 +293,8 @@ enum { ISGAME_ATTR = 0x10103f4, REQUIRED_FEATURE_ATTR = 0x1010557, REQUIRED_NOT_FEATURE_ATTR = 0x1010558, + COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED + COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED }; String8 getComponentName(String8 &pkgName, String8 &componentName) { @@ -1247,9 +1249,37 @@ int doDump(Bundle* bundle) splitName.string()).string()); } - String8 platformVersionName = AaptXml::getAttribute(tree, NULL, + String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL, "platformBuildVersionName"); - printf(" platformBuildVersionName='%s'", platformVersionName.string()); + if (platformBuildVersionName != "") { + printf(" platformBuildVersionName='%s'", platformBuildVersionName.string()); + } + + String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL, + "platformBuildVersionCode"); + if (platformBuildVersionCode != "") { + printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string()); + } + + int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree, + COMPILE_SDK_VERSION_ATTR, &error); + if (error != "") { + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:compileSdkVersion' attribute: %s", + error.string()); + goto bail; + } + if (compileSdkVersion > 0) { + printf(" compileSdkVersion='%d'", compileSdkVersion); + } + + String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree, + COMPILE_SDK_VERSION_CODENAME_ATTR, &error); + if (compileSdkVersionCodename != "") { + printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput( + compileSdkVersionCodename.string()).string()); + } + printf("\n"); int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree, diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index bd2b2a36788f..ab6dced5b67d 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -918,6 +918,22 @@ status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root) } } + + if (bundle->getCompileSdkVersion() != 0) { + if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersion", + String8::format("%d", bundle->getCompileSdkVersion()), + errorOnFailedInsert, true)) { + return UNKNOWN_ERROR; + } + } + + if (bundle->getCompileSdkVersionCodename() != "") { + if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersionCodename", + bundle->getCompileSdkVersionCodename(), errorOnFailedInsert, true)) { + return UNKNOWN_ERROR; + } + } + if (bundle->getPlatformBuildVersionCode() != "") { if (!addTagAttribute(root, "", "platformBuildVersionCode", bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { @@ -1052,7 +1068,12 @@ enum { VERSION_NAME_ATTR = 0x0101021c, }; -static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { +static ssize_t extractPlatformBuildVersion(const ResTable& table, ResXMLTree& tree, Bundle* bundle) { + // First check if we should be recording the compileSdkVersion* attributes. + static const String16 compileSdkVersionName("android:attr/compileSdkVersion"); + const bool useCompileSdkVersion = table.identifierForName(compileSdkVersionName.string(), + compileSdkVersionName.size()) != 0u; + size_t len; ResXMLTree::event_code_t code; while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { @@ -1082,6 +1103,10 @@ static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); } + if (useCompileSdkVersion && versionCode >= 0 && bundle->getCompileSdkVersion() == 0) { + bundle->setCompileSdkVersion(versionCode); + } + String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR: failed to get platform version name\n"); @@ -1091,6 +1116,11 @@ static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { bundle->setPlatformBuildVersionName(versionName); } + + if (useCompileSdkVersion && versionName != "" + && bundle->getCompileSdkVersionCodename() == "") { + bundle->setCompileSdkVersionCodename(versionName); + } return NO_ERROR; } @@ -1121,7 +1151,7 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); result = UNKNOWN_ERROR; } else { - result = extractPlatformBuildVersion(tree, bundle); + result = extractPlatformBuildVersion(assets.getResources(true), tree, bundle); } } @@ -1707,7 +1737,9 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil // extract them from the platform APK. if (packageType != ResourceTable::System && (bundle->getPlatformBuildVersionCode() == "" || - bundle->getPlatformBuildVersionName() == "")) { + bundle->getPlatformBuildVersionName() == "" || + bundle->getCompileSdkVersion() == 0 || + bundle->getCompileSdkVersionCodename() == "")) { err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); if (err != NO_ERROR) { return UNKNOWN_ERROR; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index d2aebfd31bbf..13dd93e83b64 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -15,6 +15,7 @@ */ #include <sys/stat.h> +#include <cinttypes> #include <queue> #include <unordered_map> @@ -725,6 +726,30 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } +static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) { + using namespace android; + + // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so + // we're looking for the first attribute resource in the system package. + const ResTable& table = assets.getResources(true); + Res_value val; + ssize_t idx = table.getResource(0x01010000, &val, true); + if (idx != NO_ERROR) { + // Try as a bag. + const ResTable::bag_entry* entry; + ssize_t cnt = table.lockBag(0x01010000, &entry); + if (cnt >= 0) { + idx = entry->stringBlock; + } + table.unlockBag(entry); + } + + if (idx < 0) { + return 0; + } + return table.getTableCookie(idx); +} + class LinkCommand { public: LinkCommand(LinkContext* context, const LinkOptions& options) @@ -734,7 +759,65 @@ class LinkCommand { file_collection_(util::make_unique<io::FileCollection>()) { } + void ExtractCompileSdkVersions(android::AssetManager* assets) { + using namespace android; + + int32_t cookie = FindFrameworkAssetManagerCookie(*assets); + if (cookie == 0) { + // No Framework assets loaded. Not a failure. + return; + } + + std::unique_ptr<Asset> manifest( + assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER)); + if (manifest == nullptr) { + // No errors. + return; + } + + std::string error; + std::unique_ptr<xml::XmlResource> manifest_xml = + xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error); + if (manifest_xml == nullptr) { + // No errors. + return; + } + + xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode"); + if (attr != nullptr) { + Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version; + if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) { + switch (prim->value.dataType) { + case Res_value::TYPE_INT_DEC: + compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data)); + break; + case Res_value::TYPE_INT_HEX: + compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data); + break; + default: + break; + } + } else if (String* str = ValueCast<String>(attr->compiled_value.get())) { + compile_sdk_version = *str->value; + } else { + compile_sdk_version = attr->value; + } + } + + attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName"); + if (attr != nullptr) { + Maybe<std::string>& compile_sdk_version_codename = + options_.manifest_fixer_options.compile_sdk_version_codename; + if (String* str = ValueCast<String>(attr->compiled_value.get())) { + compile_sdk_version_codename = *str->value; + } else { + compile_sdk_version_codename = attr->value; + } + } + } + // Creates a SymbolTable that loads symbols from the various APKs. + // Pre-condition: context_->GetCompilationPackage() needs to be set. bool LoadSymbolsFromIncludePaths() { auto asset_source = util::make_unique<AssetManagerSymbolSource>(); for (const std::string& path : options_.include_paths) { @@ -802,6 +885,17 @@ class LinkCommand { } else if (entry.first == kAppPackageId) { // Capture the included base feature package. included_feature_base_ = entry.second; + } else if (entry.first == kFrameworkPackageId) { + // Try to embed which version of the framework we're compiling against. + // First check if we should use compileSdkVersion at all. Otherwise compilation may fail + // when linking our synthesized 'android:compileSdkVersion' attribute. + std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName( + ResourceName("android", ResourceType::kAttr, "compileSdkVersion")); + if (symbol != nullptr && symbol->is_public) { + // The symbol is present and public, extract the android:versionName and + // android:versionCode from the framework AndroidManifest.xml. + ExtractCompileSdkVersions(asset_source->GetAssetManager()); + } } } @@ -1535,6 +1629,12 @@ class LinkCommand { context_->SetCompilationPackage(app_info.package); } + // Now that the compilation package is set, load the dependencies. This will also extract + // the Android framework's versionCode and versionName, if they exist. + if (!LoadSymbolsFromIncludePaths()) { + return 1; + } + ManifestFixer manifest_fixer(options_.manifest_fixer_options); if (!manifest_fixer.Consume(context_, manifest_xml.get())) { return 1; @@ -1563,10 +1663,6 @@ class LinkCommand { } } - if (!LoadSymbolsFromIncludePaths()) { - return 1; - } - TableMergerOptions table_merger_options; table_merger_options.auto_add_overlay = options_.auto_add_overlay; table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options); diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 3b90637d9629..1bdb762528b6 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -75,6 +75,10 @@ struct OptimizeOptions { TableFlattenerOptions table_flattener_options; Maybe<PostProcessingConfiguration> configuration; + + // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts + // are kept and will be written as output. + std::unordered_set<std::string> kept_artifacts; }; class OptimizeContext : public IAaptContext { @@ -197,9 +201,12 @@ class OptimizeCommand { if (options_.configuration && options_.output_dir) { MultiApkGenerator generator{apk.get(), context_}; - MultiApkGeneratorOptions generator_options = {options_.output_dir.value(), - options_.configuration.value(), - options_.table_flattener_options}; + MultiApkGeneratorOptions generator_options = { + options_.output_dir.value(), + options_.configuration.value(), + options_.table_flattener_options, + options_.kept_artifacts, + }; if (!generator.FromBaseApk(generator_options)) { return 1; } @@ -323,6 +330,7 @@ int Optimize(const std::vector<StringPiece>& args) { Maybe<std::string> target_abis; std::vector<std::string> configs; std::vector<std::string> split_args; + std::unordered_set<std::string> kept_artifacts; bool verbose = false; bool print_only = false; Flags flags = @@ -356,6 +364,10 @@ int Optimize(const std::vector<StringPiece>& args) { "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n" "On Windows, use a semicolon ';' separator instead.", &split_args) + .OptionalFlagList("--keep-artifacts", + "Comma separated list of artifacts to keep. If none are specified,\n" + "all artifacts will be kept.", + &kept_artifacts) .OptionalSwitch("--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", @@ -438,6 +450,14 @@ int Optimize(const std::vector<StringPiece>& args) { return 0; } + if (!kept_artifacts.empty()) { + for (const auto& artifact_str : kept_artifacts) { + for (const auto& artifact : util::Tokenize(artifact_str, ',')) { + options.kept_artifacts.insert(artifact.to_string()); + } + } + } + // Since we know that we are going to process the APK (not just print targets), make sure we // have somewhere to write them to. if (!options.output_dir) { diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index eaaefd5e099a..a68df1dbc998 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -406,6 +406,25 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { root->InsertChild(0, std::move(uses_sdk)); } + if (options_.compile_sdk_version) { + xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion"); + + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; + + attr->value = options_.compile_sdk_version.value(); + } + + if (options_.compile_sdk_version_codename) { + xml::Attribute* attr = + root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); + + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; + + attr->value = options_.compile_sdk_version_codename.value(); + } + xml::XmlActionExecutor executor; if (!BuildRules(&executor, context->GetDiagnostics())) { return false; diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index 470f65eb01c4..f5715f605b04 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -29,22 +29,42 @@ namespace aapt { struct ManifestFixerOptions { + // The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag. Maybe<std::string> min_sdk_version_default; + + // The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag. Maybe<std::string> target_sdk_version_default; + + // The Android package to use instead of the one defined in 'package' in <manifest>. + // This also renames all relative package/class names in the manifest to fully qualified + // Java names. Maybe<std::string> rename_manifest_package; + + // The Android package to use instead of the one defined in 'android:targetPackage' in + // <instrumentation>. Maybe<std::string> rename_instrumentation_target_package; + + // The version name to set if 'android:versionName' is not defined in <manifest>. Maybe<std::string> version_name_default; + + // The version code to set if 'android:versionCode' is not defined in <manifest>. Maybe<std::string> version_code_default; + + // The version of the framework being compiled against to set for 'android:compileSdkVersion' in + // the <manifest> tag. + Maybe<std::string> compile_sdk_version; + + // The version codename of the framework being compiled against to set for + // 'android:compileSdkVersionCodename' in the <manifest> tag. + Maybe<std::string> compile_sdk_version_codename; }; -/** - * Verifies that the manifest is correctly formed and inserts defaults - * where specified with ManifestFixerOptions. - */ +// Verifies that the manifest is correctly formed and inserts defaults where specified with +// ManifestFixerOptions. class ManifestFixer : public IXmlResourceConsumer { public: - explicit ManifestFixer(const ManifestFixerOptions& options) - : options_(options) {} + explicit ManifestFixer(const ManifestFixerOptions& options) : options_(options) { + } bool Consume(IAaptContext* context, xml::XmlResource* doc) override; diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 40085eab9707..1320dcd2a170 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -19,7 +19,12 @@ #include "test/Test.h" using ::android::StringPiece; +using ::testing::Eq; +using ::testing::Gt; +using ::testing::IsNull; +using ::testing::Ne; using ::testing::NotNull; +using ::testing::StrEq; namespace aapt { @@ -72,22 +77,20 @@ struct ManifestFixerTest : public ::testing::Test { }; TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) { - EXPECT_EQ(nullptr, Verify("<other-tag />")); - EXPECT_EQ(nullptr, Verify("<ns:manifest xmlns:ns=\"com\" />")); - EXPECT_NE(nullptr, Verify("<manifest package=\"android\"></manifest>")); + EXPECT_THAT(Verify("<other-tag />"), IsNull()); + EXPECT_THAT(Verify("<ns:manifest xmlns:ns=\"com\" />"), IsNull()); + EXPECT_THAT(Verify("<manifest package=\"android\"></manifest>"), NotNull()); } TEST_F(ManifestFixerTest, EnsureManifestHasPackage) { - EXPECT_NE(nullptr, Verify("<manifest package=\"android\" />")); - EXPECT_NE(nullptr, Verify("<manifest package=\"com.android\" />")); - EXPECT_NE(nullptr, Verify("<manifest package=\"com.android.google\" />")); - EXPECT_EQ(nullptr, - Verify("<manifest package=\"com.android.google.Class$1\" />")); - EXPECT_EQ(nullptr, Verify("<manifest " - "xmlns:android=\"http://schemas.android.com/apk/" - "res/android\" " - "android:package=\"com.android\" />")); - EXPECT_EQ(nullptr, Verify("<manifest package=\"@string/str\" />")); + EXPECT_THAT(Verify("<manifest package=\"android\" />"), NotNull()); + EXPECT_THAT(Verify("<manifest package=\"com.android\" />"), NotNull()); + EXPECT_THAT(Verify("<manifest package=\"com.android.google\" />"), NotNull()); + EXPECT_THAT(Verify("<manifest package=\"com.android.google.Class$1\" />"), IsNull()); + EXPECT_THAT(Verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" " + "android:package=\"com.android\" />"), + IsNull()); + EXPECT_THAT(Verify("<manifest package=\"@string/str\" />"), IsNull()); } TEST_F(ManifestFixerTest, AllowMetaData) { @@ -105,7 +108,7 @@ TEST_F(ManifestFixerTest, AllowMetaData) { </application> <instrumentation android:name=".Go"><meta-data /></instrumentation> </manifest>)EOF"); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); } TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { @@ -117,21 +120,21 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" /> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* el; xml::Attribute* attr; el = doc->root.get(); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); el = el->FindChild({}, "uses-sdk"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("7", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("7")); attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("21", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("21")); doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -139,18 +142,18 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { <uses-sdk android:targetSdkVersion="21" /> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); el = doc->root.get(); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); el = el->FindChild({}, "uses-sdk"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("8", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("8")); attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("21", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("21")); doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -158,35 +161,35 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { <uses-sdk /> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); el = doc->root.get(); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); el = el->FindChild({}, "uses-sdk"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("8", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("8")); attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("22", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("22")); doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); el = doc->root.get(); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); el = el->FindChild({}, "uses-sdk"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("8", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("8")); attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ("22", attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("22")); } TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) { @@ -197,17 +200,17 @@ TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) { <application android:name=".MainApplication" /> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* manifest_el = doc->root.get(); - ASSERT_NE(nullptr, manifest_el); + ASSERT_THAT(manifest_el, NotNull()); ASSERT_EQ("manifest", manifest_el->name); xml::Element* application_el = manifest_el->FindChild("", "application"); - ASSERT_NE(nullptr, application_el); + ASSERT_THAT(application_el, NotNull()); xml::Element* uses_sdk_el = manifest_el->FindChild("", "uses-sdk"); - ASSERT_NE(nullptr, uses_sdk_el); + ASSERT_THAT(uses_sdk_el, NotNull()); // Check that the uses_sdk_el comes before application_el in the children // vector. @@ -225,12 +228,12 @@ TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) { return child.get() == application_el; }); - ASSERT_NE(manifest_el->children.end(), uses_sdk_iter); - ASSERT_NE(manifest_el->children.end(), application_iter); + ASSERT_THAT(uses_sdk_iter, Ne(manifest_el->children.end())); + ASSERT_THAT(application_iter, Ne(manifest_el->children.end())); // The distance should be positive, meaning uses_sdk_iter comes before // application_iter. - EXPECT_GT(std::distance(uses_sdk_iter, application_iter), 0); + EXPECT_THAT(std::distance(uses_sdk_iter, application_iter), Gt(0)); } TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { @@ -247,48 +250,49 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { </application> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* manifest_el = doc->root.get(); - ASSERT_NE(nullptr, manifest_el); + ASSERT_THAT(manifest_el, NotNull()); xml::Attribute* attr = nullptr; attr = manifest_el->FindAttribute({}, "package"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("com.android"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("com.android")); xml::Element* uses_split_el = manifest_el->FindChild({}, "uses-split"); - ASSERT_NE(nullptr, uses_split_el); + ASSERT_THAT(uses_split_el, NotNull()); attr = uses_split_el->FindAttribute(xml::kSchemaAndroid, "name"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("feature_a"), attr->value); + ASSERT_THAT(attr, NotNull()); + // This should NOT have been affected. + EXPECT_THAT(attr->value, StrEq("feature_a")); xml::Element* application_el = manifest_el->FindChild({}, "application"); - ASSERT_NE(nullptr, application_el); + ASSERT_THAT(application_el, NotNull()); attr = application_el->FindAttribute(xml::kSchemaAndroid, "name"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("android.MainApplication"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("android.MainApplication")); attr = application_el->FindAttribute({}, "text"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("hello"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("hello")); xml::Element* el; el = application_el->FindChild({}, "activity"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "name"); - ASSERT_NE(nullptr, el); - EXPECT_EQ(std::string("android.activity.Start"), attr->value); + ASSERT_THAT(el, NotNull()); + EXPECT_THAT(attr->value, StrEq("android.activity.Start")); el = application_el->FindChild({}, "receiver"); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); attr = el->FindAttribute(xml::kSchemaAndroid, "name"); - ASSERT_NE(nullptr, el); - EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value); + ASSERT_THAT(el, NotNull()); + EXPECT_THAT(attr->value, StrEq("com.google.android.Receiver")); } TEST_F(ManifestFixerTest, @@ -302,19 +306,19 @@ TEST_F(ManifestFixerTest, <instrumentation android:name=".TestRunner" android:targetPackage="android" /> </manifest>)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* manifest_el = doc->root.get(); - ASSERT_NE(nullptr, manifest_el); + ASSERT_THAT(manifest_el, NotNull()); xml::Element* instrumentation_el = manifest_el->FindChild({}, "instrumentation"); - ASSERT_NE(nullptr, instrumentation_el); + ASSERT_THAT(instrumentation_el, NotNull()); xml::Attribute* attr = instrumentation_el->FindAttribute(xml::kSchemaAndroid, "targetPackage"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("com.android"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("com.android")); } TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) { @@ -326,41 +330,39 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)EOF", options); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* manifest_el = doc->root.get(); - ASSERT_NE(nullptr, manifest_el); + ASSERT_THAT(manifest_el, NotNull()); xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("Beta"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Beta")); attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); - ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::string("0x10000000"), attr->value); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x10000000")); } TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) { - EXPECT_EQ(nullptr, - Verify("<manifest package=\"android\" coreApp=\"hello\" />")); - EXPECT_EQ(nullptr, - Verify("<manifest package=\"android\" coreApp=\"1dp\" />")); + EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"hello\" />"), IsNull()); + EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"1dp\" />"), IsNull()); std::unique_ptr<xml::XmlResource> doc = Verify("<manifest package=\"android\" coreApp=\"true\" />"); - ASSERT_NE(nullptr, doc); + ASSERT_THAT(doc, NotNull()); xml::Element* el = doc->root.get(); - ASSERT_NE(nullptr, el); + ASSERT_THAT(el, NotNull()); - EXPECT_EQ("manifest", el->name); + EXPECT_THAT(el->name, StrEq("manifest")); xml::Attribute* attr = el->FindAttribute("", "coreApp"); - ASSERT_NE(nullptr, attr); + ASSERT_THAT(attr, NotNull()); - EXPECT_NE(nullptr, attr->compiled_value); - EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get())); + EXPECT_THAT(attr->compiled_value, NotNull()); + EXPECT_THAT(ValueCast<BinaryPrimitive>(attr->compiled_value.get()), NotNull()); } TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) { @@ -375,21 +377,21 @@ TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) { <uses-feature android:glEsVersion="2" /> </feature-group> </manifest>)EOF"; - EXPECT_NE(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), NotNull()); input = R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <uses-feature android:name="feature" android:glEsVersion="1" /> </manifest>)EOF"; - EXPECT_EQ(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), IsNull()); input = R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <uses-feature /> </manifest>)EOF"; - EXPECT_EQ(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), IsNull()); input = R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -398,7 +400,7 @@ TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) { <uses-feature android:name="feature" android:glEsVersion="1" /> </feature-group> </manifest>)EOF"; - EXPECT_EQ(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), IsNull()); input = R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -407,7 +409,7 @@ TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) { <uses-feature /> </feature-group> </manifest>)EOF"; - EXPECT_EQ(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), IsNull()); } TEST_F(ManifestFixerTest, IgnoreNamespacedElements) { @@ -416,7 +418,7 @@ TEST_F(ManifestFixerTest, IgnoreNamespacedElements) { package="android"> <special:tag whoo="true" xmlns:special="http://google.com" /> </manifest>)EOF"; - EXPECT_NE(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), NotNull()); } TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) { @@ -425,7 +427,7 @@ TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) { package="android"> <tag whoo="true" /> </manifest>)EOF"; - EXPECT_EQ(nullptr, Verify(input)); + EXPECT_THAT(Verify(input), IsNull()); } TEST_F(ManifestFixerTest, SupportKeySets) { @@ -446,4 +448,23 @@ TEST_F(ManifestFixerTest, SupportKeySets) { EXPECT_THAT(Verify(input), NotNull()); } +TEST_F(ManifestFixerTest, InsertCompileSdkVersions) { + std::string input = R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)"; + ManifestFixerOptions options; + options.compile_sdk_version = {"28"}; + options.compile_sdk_version_codename = {"P"}; + + std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); + ASSERT_THAT(manifest, NotNull()); + + xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); +} + } // namespace aapt diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 3c96344d8602..da3b8792be69 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -137,6 +137,10 @@ bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { const StringPiece ext = file::GetExtension(apk_name); const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string())); + std::unordered_set<std::string> artifacts_to_keep = options.kept_artifacts; + std::unordered_set<std::string> filtered_artifacts; + std::unordered_set<std::string> kept_artifacts; + // For now, just write out the stripped APK since ABI splitting doesn't modify anything else. for (const Artifact& artifact : config.artifacts) { SourcePathDiagnostics diag{{apk_name}, context_->GetDiagnostics()}; @@ -163,6 +167,20 @@ bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { ContextWrapper wrapped_context{context_}; wrapped_context.SetSource({artifact_name}); + if (!options.kept_artifacts.empty()) { + const auto& it = artifacts_to_keep.find(artifact_name); + if (it == artifacts_to_keep.end()) { + filtered_artifacts.insert(artifact_name); + if (context_->IsVerbose()) { + context_->GetDiagnostics()->Note(DiagMessage(artifact_name) << "skipping artifact"); + } + continue; + } else { + artifacts_to_keep.erase(it); + kept_artifacts.insert(artifact_name); + } + } + std::unique_ptr<ResourceTable> table = FilterTable(artifact, config, *apk_->GetResourceTable(), &wrapped_context, &filters); if (!table) { @@ -197,6 +215,30 @@ bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { } } + // Make sure all of the requested artifacts were valid. If there are any kept artifacts left, + // either the config or the command line was wrong. + if (!artifacts_to_keep.empty()) { + context_->GetDiagnostics()->Error( + DiagMessage() << "The configuration and command line to filter artifacts do not match"); + + context_->GetDiagnostics()->Error(DiagMessage() << kept_artifacts.size() << " kept:"); + for (const auto& artifact : kept_artifacts) { + context_->GetDiagnostics()->Error(DiagMessage() << " " << artifact); + } + + context_->GetDiagnostics()->Error(DiagMessage() << filtered_artifacts.size() << " filtered:"); + for (const auto& artifact : filtered_artifacts) { + context_->GetDiagnostics()->Error(DiagMessage() << " " << artifact); + } + + context_->GetDiagnostics()->Error(DiagMessage() << artifacts_to_keep.size() << " missing:"); + for (const auto& artifact : artifacts_to_keep) { + context_->GetDiagnostics()->Error(DiagMessage() << " " << artifact); + } + + return false; + } + return true; } diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h index c9b4be88685d..dedb610e1caa 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.h +++ b/tools/aapt2/optimize/MultiApkGenerator.h @@ -17,6 +17,10 @@ #ifndef AAPT2_APKSPLITTER_H #define AAPT2_APKSPLITTER_H +#include <memory> +#include <string> +#include <unordered_set> + #include "Diagnostics.h" #include "LoadedApk.h" #include "configuration/ConfigurationParser.h" @@ -27,6 +31,7 @@ struct MultiApkGeneratorOptions { std::string out_dir; configuration::PostProcessingConfiguration config; TableFlattenerOptions table_flattener_options; + std::unordered_set<std::string> kept_artifacts; }; /** diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index 4a2181e0b344..b676efb8d025 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -199,6 +199,10 @@ class AssetManagerSymbolSource : public ISymbolSource { std::unique_ptr<SymbolTable::Symbol> FindByReference( const Reference& ref) override; + android::AssetManager* GetAssetManager() { + return &assets_; + } + private: android::AssetManager assets_; diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index b0cf44accafa..fddb6b8c5d87 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -418,6 +418,15 @@ const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece return nullptr; } +Attribute* Element::FindOrCreateAttribute(const StringPiece& ns, const StringPiece& name) { + Attribute* attr = FindAttribute(ns, name); + if (attr == nullptr) { + attributes.push_back(Attribute{ns.to_string(), name.to_string()}); + attr = &attributes.back(); + } + return attr; +} + Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) { return FindChildWithAttribute(ns, name, {}, {}, {}); } diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h index cf06ba5cc50e..8f3829611f30 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -100,6 +100,8 @@ class Element : public Node { Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name); const Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name) const; + Attribute* FindOrCreateAttribute(const android::StringPiece& ns, + const android::StringPiece& name); Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name); const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const; diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index fd42033d839c..421e54558df4 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -940,11 +940,14 @@ def verify_callback_handlers(clazz): for f in found.values(): takes_handler = False + takes_exec = False for m in by_name[f.name]: if "android.os.Handler" in m.args: takes_handler = True - if not takes_handler: - warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler") + if "java.util.concurrent.Executor" in m.args: + takes_exec = True + if not takes_exec: + warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Executor") def verify_context_first(clazz): @@ -968,7 +971,7 @@ def verify_listener_last(clazz): for a in m.args: if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"): found = True - elif found and a != "android.os.Handler": + elif found and a != "android.os.Handler" and a != "java.util.concurrent.Executor": warn(clazz, m, "M3", "Listeners should always be at end of argument list") @@ -1218,6 +1221,18 @@ def verify_method_name_not_kotlin_operator(clazz): unique_binary_op(m, m.name[:-6]) # Remove 'Assign' suffix +def verify_collections_over_arrays(clazz): + """Warn that [] should be Collections.""" + + safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"] + for m in clazz.methods: + if m.typ.endswith("[]") and m.typ not in safe: + warn(clazz, m, None, "Method should return Collection<> (or subclass) instead of raw array") + for arg in m.args: + if arg.endswith("[]") and arg not in safe: + warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array") + + def examine_clazz(clazz): """Find all style issues in the given class.""" @@ -1260,7 +1275,7 @@ def examine_clazz(clazz): verify_manager(clazz) verify_boxed(clazz) verify_static_utils(clazz) - verify_overload_args(clazz) + # verify_overload_args(clazz) verify_callback_handlers(clazz) verify_context_first(clazz) verify_listener_last(clazz) @@ -1274,6 +1289,7 @@ def examine_clazz(clazz): verify_closable(clazz) verify_member_name_not_kotlin_keyword(clazz) verify_method_name_not_kotlin_operator(clazz) + verify_collections_over_arrays(clazz) def examine_stream(stream): diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index abbb82a9cc69..0dd964c0ef46 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -164,12 +164,6 @@ interface IWifiManager void enableAggressiveHandover(int enabled); int getAggressiveHandover(); - void setAllowScansWithTraffic(int enabled); - int getAllowScansWithTraffic(); - - boolean setEnableAutoJoinWhenAssociated(boolean enabled); - boolean getEnableAutoJoinWhenAssociated(); - void enableWifiConnectivityManager(boolean enabled); WifiConnectionStatistics getConnectionStatistics(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 558004ce1753..a158d94b646e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -3488,27 +3488,23 @@ public class WifiManager { } /** - * Set setting for allowing Scans when traffic is ongoing. + * Deprecated + * Does nothing * @hide + * @deprecated */ public void setAllowScansWithTraffic(int enabled) { - try { - mService.setAllowScansWithTraffic(enabled); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return; } /** - * Get setting for allowing Scans when traffic is ongoing. + * Deprecated + * returns value for 'disabled' * @hide + * @deprecated */ public int getAllowScansWithTraffic() { - try { - return mService.getAllowScansWithTraffic(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return 0; } /** @@ -3538,29 +3534,23 @@ public class WifiManager { } /** - * Framework layer autojoin enable/disable when device is associated - * this will enable/disable autojoin scan and switch network when connected - * @return true -- if set successful false -- if set failed + * Deprecated + * returns false * @hide + * @deprecated */ public boolean setEnableAutoJoinWhenAssociated(boolean enabled) { - try { - return mService.setEnableAutoJoinWhenAssociated(enabled); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return false; } /** - * Get setting for Framework layer autojoin enable status + * Deprecated + * returns false * @hide + * @deprecated */ public boolean getEnableAutoJoinWhenAssociated() { - try { - return mService.getEnableAutoJoinWhenAssociated(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return false; } /** |